128 lines
5.0 KiB
Plaintext
128 lines
5.0 KiB
Plaintext
在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,
|
||
并且reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。
|
||
1.TypeOf
|
||
typeof的对象中可以使用两个方法 t.name t.kind
|
||
其中t.name是自定义命名的名字,而t.kind是底层的类型
|
||
比如
|
||
type cat struct{} 这个name就是cat 而 kind就是struct
|
||
|
||
2.ValueOf
|
||
reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。
|
||
v对象同样也有kind,但没有name
|
||
kind对象可以取到值的类型并转换成对应的原始值
|
||
例如:
|
||
func reflectValue(x interface{}) {
|
||
v := reflect.ValueOf(x)
|
||
k := v.Kind()
|
||
switch k {
|
||
case reflect.Int64:
|
||
// v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换
|
||
fmt.Printf("type is int64, value is %d\n", int64(v.Int()))
|
||
case reflect.Float32:
|
||
// v.Float()从反射中获取浮点型的原始值,然后通过float32()强制类型转换
|
||
fmt.Printf("type is float32, value is %f\n", float32(v.Float()))
|
||
case reflect.Float64:
|
||
// v.Float()从反射中获取浮点型的原始值,然后通过float64()强制类型转换
|
||
fmt.Printf("type is float64, value is %f\n", float64(v.Float()))
|
||
}
|
||
}
|
||
|
||
通过反射设置变量的值
|
||
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。
|
||
比如
|
||
func reflectSetValue1(x interface{}) {
|
||
v := reflect.ValueOf(x)
|
||
if v.Kind() == reflect.Int64 {
|
||
v.SetInt(200) //修改的是副本,reflect包会引发panic
|
||
}
|
||
}
|
||
func reflectSetValue2(x interface{}) {
|
||
v := reflect.ValueOf(x)
|
||
// 反射中使用 Elem()方法获取指针对应的值
|
||
if v.Elem().Kind() == reflect.Int64 {
|
||
v.Elem().SetInt(200)
|
||
}
|
||
}
|
||
func main() {
|
||
var a int64 = 100
|
||
// reflectSetValue1(a) //panic: reflect: reflect.Value.SetInt using unaddressable value
|
||
reflectSetValue2(&a) //传递a的指针,修改原始值
|
||
fmt.Println(a)
|
||
}
|
||
|
||
func (v Value) IsNil() bool
|
||
报告v持有的值是否为nil。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic。
|
||
func (v Value) IsValid() bool
|
||
返回v是否持有一个值。如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。
|
||
|
||
|
||
func main() {
|
||
// *int类型空指针
|
||
var a *int
|
||
fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
|
||
// nil值
|
||
fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
|
||
// 实例化一个匿名结构体
|
||
b := struct{}{}
|
||
// 尝试从结构体中查找"abc"字段
|
||
fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
|
||
// 尝试从结构体中查找"abc"方法
|
||
fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
|
||
// map
|
||
c := map[string]int{}
|
||
// 尝试从map中查找一个不存在的键
|
||
fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
|
||
}
|
||
|
||
|
||
结构体反射
|
||
type student struct {
|
||
Name string `json:"name"`
|
||
Score int `json:"score"`
|
||
}
|
||
|
||
|
||
结构体划分为几部分
|
||
stu1 := student{
|
||
Name: "小王子",
|
||
Score: 90,
|
||
}
|
||
比如这个结构体,对typeof这个函数来说返回的对象是t 其中两个方法 是 t.name (student)和 t.kind(struct)
|
||
t.NumField()会返回结构体中的字段数目是2
|
||
所以有:
|
||
遍历所有结构体字段:
|
||
for i:=0;i<t.NumField();i++{
|
||
field:=t.field(i)
|
||
fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json"))
|
||
}
|
||
对结构体来说,t这个对象的返回值中存在以下类型,通过t.field(索引)获取:
|
||
type StructField struct {
|
||
// Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
|
||
// 参见http://golang.org/ref/spec#Uniqueness_of_identifiers
|
||
Name string
|
||
PkgPath string
|
||
Type Type // 字段的类型
|
||
Tag StructTag // 字段的标签
|
||
Offset uintptr // 字段在结构体中的字节偏移量
|
||
Index []int // 用于Type.FieldByIndex时的索引切片
|
||
Anonymous bool // 是否匿名字段
|
||
}
|
||
比如当第一次循环时,StructField中能找到的信息是
|
||
Name : Name
|
||
PkgPath : ""
|
||
Type :string
|
||
Tag : json:“name”
|
||
offset: 0
|
||
index: [0]
|
||
Anonymous:false
|
||
同时t对象还可以通过方法FieldByName去找到对应的字段
|
||
比如
|
||
if scoreField, ok := t.FieldByName("Score"); ok {
|
||
fmt.Printf("name:%s index:%d type:%v json tag:%v\n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("json"))
|
||
}
|
||
返回的就是结构体中第二个字段的信息
|
||
v对象也有FieldByName,找到的是对应字段的值信息
|
||
比如
|
||
wangzi := v.FieldByName("Name")
|
||
fmt.Println(wangzi)
|
||
取到的是小王子 即通过字段名获取对应的值 |