反射
Go语言提供了一种机制在运行时更新和检查变量的值、调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射。
Go语言中的反射是由reflect包提供支持的,它定义了两个重要的类型Type和Value任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,并且reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。
Type和Value
Type是interface类型。
type Type interface {
// Methods applicable to all types.
// 返回该类型的值分配的字节数
Align() int
// 返回该类型的值作为字段时的字节数
FieldAlign() int
// 返回该类型方法集中的第i个方法
// 如果i不在[0, NumMethod()]范围内,会panic
// 对于非interface类型T或*T,返回的Method的Type和Func字段描述了一个函数,其第一个参数是接收器,并且只有导出的方法可以访问
// 对于interface类型,返回的Method的Type字段给出了方法的签名,没有接收器,Func字段为nil
// 方法的排序按照词典序
Method(int) Method
// 返回该类型指定名字的方法,以及一个布尔值指示方法是否存在
// 对于非interface类型T或*T,返回的Method的Type和Func字段描述了一个函数,其第一个参数是接收器
// 对于interface类型,返回的Method的Type字段给出了方法的签名,没有接收器,Func字段为nil
MethodByName(string) (Method, bool)
// 返回使用Method可以访问的方法的数量
// 注意:NumMethod只对interface类型计算了未导出的方法
NumMethod() int
// 返回该类型的名称,未定义名称时返回空串
Name() string
// 返回类型的包名称
// 如果该类型是predeclared或未定义的,将返回空串
PkgPath() string
// 返回该类型值的字节数
Size() uintptr
// 返回该类型的字符串表示
String() string
// 返回该类型的具体种类
Kind() Kind
// 返回该类型是否实现了接口u
Implements(u Type) bool
// 返回该类型是否可以被分配给u
AssignableTo(u Type) bool
// 返回该类型是否可以转为u类型
// 即使返回true仍可能发生panic
ConvertibleTo(u Type) bool
// 返回该类型的值是否具有可比性
// 即使返回true仍可能发生panic
Comparable() bool
// 适用于某些种类的方法
// 每个种类允许的方法:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// 返回类型的比特大小
// 如果不是以下种类就会发生panic
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// 返回通道的方向
// 非chan类型会导致panic
ChanDir() ChanDir
// 返回一个函数类型的最后一个入参数能否是可变参数
// 如果是, t.In(t.NumIn() - 1) 返回参数的实际类型 []T.
//
// 如果类型的种类kind不是函数function就会panic
IsVariadic() bool
// 返回一个类型的元素类型
// 如果不是 Array, Chan, Map, Ptr, or Slice类型则会panic
Elem() Type
// 返回结构体的第i个字段
// 如果该类型的种类不是Struct会panic.
// 如果i越界会panic
Field(i int) StructField
// 返回索引序列的嵌套字段
// 如果该类型的种类不是Struct会panic.
FieldByIndex(index []int) StructField
// 返回指定名称的字段
FieldByName(name string) (StructField, bool)
// 返回满足指定匹配函数的字段
// 以广度优先方式进行搜索,如果当前深度有多个满足match方法的字段,则match方法返回false
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 返回该类型第i个入参
// 如果该类型的种类不是Func会panic
// 如果i越界会panic
In(i int) Type
// 返回map的key类型
// 该类型不是Map会panic
Key() Type
// 返回数组的长度
// 不是数组会panic
Len() int
// 返回结构体的字段数量
// 如果种类不是结构体会panic
NumField() int
// 返回入参数量
// 不是Func种类会panic
NumIn() int
// 返回出参数量
// 不是Func种类会panic
NumOut() int
// 返回第i个出参的类型
// 如果该类型的种类不是Func会panic
// 如果i越界会panic
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
Type是struct类型。
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}
反射的常见用法
根据类型的不同
使用反射很常见的一个场景就是根据类型的不同做不同处理。
func getType(i interface{}) {
v := reflect.ValueOf(i)
switch v.Kind() {
case reflect.Bool:
fmt.Println(v.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Println(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
fmt.Println(v.Uint())
case reflect.Float32, reflect.Float64:
fmt.Println(v.Float())
case reflect.String:
fmt.Println(v.String())
case reflect.Interface:
fmt.Println(v.Interface())
case reflect.Struct:
fmt.Println(v.Interface())
case reflect.Map:
fmt.Println(v.Interface())
case reflect.Slice:
fmt.Println(v.Interface())
case reflect.Array:
fmt.Println(v.Interface())
case reflect.Ptr:
fmt.Println(v.Interface())
case reflect.Chan:
fmt.Println(v.Interface())
default:
fmt.Println("unknown")
}
}
标准json库中的示例
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(marshalerType) {
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
if t.Implements(marshalerType) {
return marshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr && reflect.PtrTo(t).Implements(textMarshalerType) {
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
if t.Implements(textMarshalerType) {
return textMarshalerEncoder
}
switch t.Kind() {
case reflect.Bool:
return boolEncoder
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intEncoder
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintEncoder
case reflect.Float32:
return float32Encoder
case reflect.Float64:
return float64Encoder
case reflect.String:
return stringEncoder
case reflect.Interface:
return interfaceEncoder
case reflect.Struct:
return newStructEncoder(t)
case reflect.Map:
return newMapEncoder(t)
case reflect.Slice:
return newSliceEncoder(t)
case reflect.Array:
return newArrayEncoder(t)
case reflect.Ptr:
return newPtrEncoder(t)
default:
return unsupportedTypeEncoder
}
}
通过反射修改数据/调用方法
【修改时使用指针取Value,遍历时使用非指针取Value。】
基本类型
基本类型指: int*、uint*、float*、complex*、bool这些类型。
func testBaseKind() {
a := 1
v := reflect.ValueOf(&a) // 修改必须要传指针
v.Elem().SetInt(2)
fmt.Println(a)
}
数组类型
func testArray() {
arr := [3]int{1, 2, 3}
v := reflect.ValueOf(&arr)
v.Elem().Index(2).SetInt(9)
fmt.Println(arr[2])
}
chan类型
通过反射来向chan中发送数据,也可以从chan中接收数据
func testChan() {
ch := make(chan int, 1)
v := reflect.ValueOf(&ch)
v.Elem().Send(reflect.ValueOf(2))
fmt.Println(<-ch)
}
map类型
func testMap() {
m := map[string]int{"a": 1, "b": 2}
v := reflect.ValueOf(&m)
v.Elem().SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(2))
fmt.Println(m["a"])
// map迭代器, 不能用指针
v2 := reflect.ValueOf(m)
iter := v2.MapRange()
for iter.Next() {
fmt.Println(iter.Key(), iter.Value())
}
}
slice类型
func testSlice() {
sli := []int{1, 2, 3}
v := reflect.ValueOf(&sli)
v.Elem().Index(2).SetInt(4)
fmt.Println(sli[2])
}
string类型
func testString() {
s := "abc"
v := reflect.ValueOf(&s)
v.Elem().SetString("efg")
fmt.Println(s)
}
结构体反射
type A struct {
a string
B string
C *A
}
func testStruct() {
a := A{a: "1", B: "2", C: &A{a: "3", B: "4"}}
v := reflect.ValueOf(a)
// 遍历字段
for i := 0; i < v.NumField(); i++ {
fmt.Println(v.Field(i))
}
// 只有导出的字段才可以调Interface()方法
fmt.Println(v.Field(1).Interface())
fmt.Println(v.FieldByName("a"))
fmt.Println(v.FieldByIndex([]int{2, 1}).Interface())
}