【Let's Go!】 反射

针对Go SDK 1.16.6

1. 反射

1.1. Type

Type:src/reflect/type.go:38

type Type interface {
    Align() int
    
    FieldAlign() int
    
    Method(int) Method
    
    MethodByName(string) (Method, bool)
    
    NumMethod() int
    
    Name() string
    
    ...
    
    // Elem returns a type's element type.
    // It panics if the type's Kind is not Array, Chan, Map, Ptr, or Slice.
    Elem() Type
    
    Field(i int) StructField
    
    FieldByIndex(index []int) StructField
    
    FieldByName(name string) (StructField, bool)
    
    FieldByNameFunc(match func(string) bool) (StructField, bool)
}

优化小技巧:内存对齐,巧妙的排列结构体的字段顺序,可以有效节省对象占用的内存大小

type Part1 struct {
    a bool
    b int32
    c int8
    d int64
    e byte
}

type Part2 struct {
    e byte
    c int8
    a bool
    b int32
    d int64
}

func main() {
    part1 := Part1{}
    part2 := Part2{}

    fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1))
    fmt.Printf("part2 size: %d, align: %d\n", unsafe.Sizeof(part2), unsafe.Alignof(part2))
}

// 输出
//part1 size: 32, align: 8
//part2 size: 16, align: 8

Type本身是可比较的

type A struct {
    b     bool
    Name  int64
    Grade int
}

func main() {
    a1 := new(A)
    a2 := new(A)
    mp := make(map[reflect.Type]int)
    mp[reflect.TypeOf(a1)] = 1
    fmt.Println(mp)

    mp[reflect.TypeOf(a2)] = 2
    fmt.Println(mp)
}

// 输出
//map[*reflect_study.A:1]
//map[*reflect_study.A:2]

1.2. Value

type Value struct {
  
}

1.3. Kind

// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint

const (
   Invalid Kind = iota
   Bool
   Int
   Int8
   Int16
   Int32
   Int64
   Uint
   Uint8
   Uint16
   Uint32
   Uint64
   Uintptr
   Float32
   Float64
   Complex64
   Complex128
   Array
   Chan
   Func
   Interface
   Map
   Ptr
   Slice
   String
   Struct
   UnsafePointer
)

1.4. 三大法则

  1. interface{} 变量可以反射出反射对象;
  2. 从反射对象可以获取 interface{} 变量;
  3. 要修改反射对象,其值必须可设置;

通过TypeOf和ValueOf获取反射对象

func main() {
    str := "abcd"
    fmt.Println(reflect.TypeOf(str))
    fmt.Println(reflect.ValueOf(str))
}

反射对象转化为interface{}

func main() {
    v := reflect.ValueOf(1)
    i := v.Interface().(int)
    fmt.Println(i)
}

golang-bidirectional-reflection

要修改反射对象,其值必须可修改

func main() {
    //i := 1
    //v:=reflect.ValueOf(i)

    // 报错,原因:go都是值传递,反射对象v和基本类型i没有关系,所以修改反射对象v的值对基本类型i无影响,程序为了防止错误就会崩溃
    //v.SetInt(2222)

    //fmt.Println(i)

    i := 1
    v := reflect.ValueOf(&i)
    v.Elem().SetInt(100)
    fmt.Println(i)
}

1.5. 实现方式

type TestHandler struct {
}

func (t *TestHandler) Error() string {
		panic("implement me")
}

func main() {
    testError := reflect.TypeOf((*error)(nil)).Elem()

    test := reflect.TypeOf(TestHandler{})
    testPtr := reflect.TypeOf(&TestHandler{})

    println(test.Implements(testError))
    println(testPtr.Implements(testError))
}

Implements源码实现细节

接口的方法和实现类的方法进行比对,复杂度O(n)

func (t *rtype) Implements(u Type) bool {
    if u == nil {
      panic("reflect: nil type passed to Type.Implements")
    }
    if u.Kind() != Interface {
      panic("reflect: non-interface type passed to Type.Implements")
    }
    return implements(u.(*rtype), t)
}

func implements(T, V *rtype) bool {
    if T.Kind() != Interface {
      return false
    }
    t := (*interfaceType)(unsafe.Pointer(T))
    if len(t.methods) == 0 {
      return true
    }

    if V.Kind() == Interface {
      v := (*interfaceType)(unsafe.Pointer(V))
      i := 0
      for j := 0; j < len(v.methods); j++ {
        tm := &t.methods[i]
        tmName := t.nameOff(tm.name)
        vm := &v.methods[j]
        vmName := V.nameOff(vm.name)
        if vmName.name() == tmName.name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) {
          if !tmName.isExported() {
            tmPkgPath := tmName.pkgPath()
            if tmPkgPath == "" {
              tmPkgPath = t.pkgPath.name()
            }
            vmPkgPath := vmName.pkgPath()
            if vmPkgPath == "" {
              vmPkgPath = v.pkgPath.name()
            }
            if tmPkgPath != vmPkgPath {
              continue
            }
          }
          if i++; i >= len(t.methods) {
            return true
          }
        }
      }
      return false
    }
  
 		......
}

1.6. 调用函数

样例:使用反射调用函数

func Add(a, b int) int {
		return a + b
}

func main() {
    v := reflect.ValueOf(Add)
    if v.Kind() != reflect.Func {
      return
    }
    t := v.Type()
    argv := make([]reflect.Value, t.NumIn())

    for i := range argv {
      if t.In(i).Kind() != reflect.Int {
        return
      }
      argv[i] = reflect.ValueOf(i)
    }
    result := v.Call(argv)
    if len(result) != 0 && result[0].Kind() != reflect.Int {
      return
    }
    println(result[0].Int())
}

1.7. 参考文献

Go语言设计与实现 反射
在 Go 中恰到好处的内存对齐

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×