Golang反射

66 阅读5分钟

反射

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())
}