- 反射机制:在编译时不知道类型的情况下,可更新变量,在运行时查看值,调用方法以及直接对它们的布局进行操作
-
一个函数统一处理各种类型,而这些类型可能:
-
无法共享某个接口
-
布局未知
-
在设计函数时还不存在
-
-
reflect.TypeOf 总是返回接口值的动态类型, 类型为 reflect.Type
-
reflect.TypeOf 总是返回接口值的具体值,类型为 reflect.Value
-
Value 类型的 Type 方法把类型以 reflect.Type 方式返回。Inerface 方法返回一个空接口 interface{}
-
Value 类型的 Kind 方法返回类型的分类(kind),只有少数几种值,见 go doc inflect.Invalid
-
用 switch v.Kind() 匹配 kind
-
尽可能避免在包的 API 里暴露反射相关的内容,而是定义一个未导出的函数做真正的处理,再导出一个简单的封装函数
-
用 Value.Kind() 判断值是否属于复杂类型:reflet.Slice/Array/Struct/Map/Ptr/Interface。对于不同类型,调用相应方法获取内部信息:
-
slice 与数组:Len() 方法返回元素个数,Index(i) 方法返回第 i 个元素,类型为 reflect.Value
-
结构体: NumField() 方法返回字段数,Field(i) 返回第 i 个字段,类型为 relect.Value
-
map: MapKeys() 返回键集合 []reflect.Value,MapIndex(key) 返回 key 对应的值
-
指针:Elem() 方法返回而指针指向的变量,类型为 reflect.Value。如果指针为 nil, 返回结果的类型为 Invalid。也可以用 IsNil() 方法检测
-
接口:用 IsNil() 方法检测 nil,用Elem() 方法获取动态值
-
-
非导出字段在反射下也是可见的
- 参考书 P264 代码
-
通过 CanAddr() 方法来询问 relect.Value 类型的变量是否可寻址
-
从一个可寻址的 reflect.Value 值获取变量需要 3 步:1,调用 Addr() 返回一个包含变量指针的 Value;2,在这个 Value 上调用Interface(),返回一个包含这个指针的 interface {} 值;3,如果知道变量的类型,用类型断言把接口内容转换为一个普通指针,用这个指针来更新变量。
x := 2 d := reflect.ValueOf(&x).Elem() //d 代表变量 x,Elem() 方法返回指针指向的变量 px := d.Addr().Interface().(*int) // 1,2,3步下来:px := &x *px = 3 // x = 3
-
如果可以寻址,也可以通过 Value 的 Set 方法来更新变量:
d.Set(reflect.ValueOf(4)) // 上面例子中的d
-
Set 会检查可赋值性条件(通常由编译器检查)。类型不匹配或者 Value 值不可寻址会导致崩溃
-
有一些基本类型特化的 Set 方法变种:SetInt,SetUint,SetString,SetFloat 等。有一定容错性,但在指向 interface{} 类型的 Value 上调用会崩溃
-
一个未导出的字段不允许修改,用 CanSet 方法报告一个 Value 是否可寻址且可更改
-
Unmarshal 函数使用反射来修改已经存在的结构体变量字段
-
reflect.New(Type) Value 返回 Type类型Value的零值指针
-
示例见P268
-
reflect.Type 的 Field(i) 方法返回第 i 个字段,类型为 reflect.StructField。这个类型提供了字段的名称,类型以及可选的标签(Tag)字段
-
Tag 字段类型为 reflect.StructTag,底层类型为字符串。提供Get(key)方法返回标签字符串中key对应的子串
-
Type 和 Value 都有 Method(i) 方法。Type.Method 返回第 i 个reflect.Method 类型的实例; Value.Method(i) 返回 reflect.Value,代表一个方法值(已绑定接收者)
-
reflect.Value.Call 方法可以调用Func类型的Value
-
反射的功能和表达能力都很强,但是要谨慎使用,因为:
-
编译器报告类型的错误,在反射中都有对应的误用方法,导致错误只能在运行时发生。应确保反射封装在包里,包的API中避免使用 reflect.Value
-
反射降低了自动重构和分析工具的安全性和准确度,因为无法检测到类型信息
-
大量使用反着的代码很难理解。对于接受 interface{} 或者 reflect.Value 的函数,要写清楚期望的参数类型和其他限制条件
-
基于反射的函数比为特定类型优化的函数要慢。
-