暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Go语言之反射

欢乐毅城 2021-07-26
213

注:每天进步一点点,记录每日成长,思考的第06/365天 ;  

本文预计阅读时间: 10分钟


1


01


反射定义

反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。

维基百科

   Go 语言中定义的反射,是编译时不确定这些变量的具体类型,在运行时检测和更新它们的值、调用它们的方法,这就是放射机制。


1


02


常用场景

   1)编写一个函数时,参数类型是不确定的;

   2)有时需要根据某些条件决定调用哪个函数,比如根据用户输入。


1


03


Go的反射reflect

Go的反射机制是由reflect包提供的,它定义了一个接口和一个结构体,即 reflect.Type
reflect.Value
,它们提供很多函数来获取存储在接口里的类型信息。


并且 reflect 包提供了 reflect.TypeOf 和 reflect.ValueOf 两个函数来获取任意对象的 
reflect.Type 和 reflect.Value

func  TypeOf (i  interface {})  Type  

func  ValueOf (i  interface {})  Value

1)TypeOf

调用TypeOf 函数,实参会转化为interface{ } 类型,然后实参的类型信息,方法集、值信息都会被存储到interface{ } 变量中,返回Type。


Type实际上是一个接口,它定了很多方法,用于获取类型相关的各种信息。

type Type interface { 
  //此类型的变量对齐后所占用的字节数
  Align() int
   
  // 如果是 struct 的字段,对齐后占用的字节数   
FieldAlign() int
 
// 返回类型方法集里的第 `i` (传入的参数)个方法  
Method(int) Method

// 通过名称获取方法
MethodByName(string) (Method, bool)
    
// 获取类型方法集里导出的方法个数
NumMethod() int

// 类型名称   
Name() string

// 返回类型所在的路径,如:encoding/base64   
PkgPath() string
 
// 返回类型的大小,和 unsafe.Sizeof 功能类似
Size() uintptr
  
// 返回类型的字符串表示形式  
String() string
 
// 返回类型的类型值  
Kind() Kind

// 类型是否实现了接口 u  
Implements(u Type) bool
  
// 是否可以赋值给 u  
AssignableTo(u Type) bool

// 是否可以类型转换成 u  
ConvertibleTo(u Type) bool
  
// 类型是否可以比较 
Comparable() bool
  
// 下面这些函数只有特定类型可以调用
// 如:Key, Elem 两个方法就只能是 Map 类型才能调用

// 类型所占据的位数
Bits() int

// 返回通道的方向,只能是 chan 类型调用 
ChanDir() ChanDir

// 返回类型是否是可变参数,只能是 func 类型调用
// 比如 t 是类型 func(x int, y ... float64)
// 那么 t.IsVariadic() == true
IsVariadic() bool

// 返回内部子元素类型,只能由类型 Array, Chan, Map, Ptr, or Slice 调用 
Elem() Type
    
// 返回结构体类型的第 i 个字段,只能是结构体类型调用
// 如果 i 超过了总字段数,就会 panic  
Field(i int) StructField

// 返回嵌套的结构体的字段
FieldByIndex(index []int) StructField
 
// 通过字段名称获取字段  
FieldByName(name string) (StructField, bool)

// FieldByNameFunc returns the struct field with a name
// 返回名称符合 func 函数的字段   
FieldByNameFunc(match func(string) bool) (StructField, bool)
    
// 获取函数类型的第 i 个参数的类型   
In(i int) Type

// 返回 map 的 key 类型,只能由类型 map 调用
Key() Type
    
// 返回 Array 的长度,只能由类型 Array 调用  Len() int
  
// 返回类型字段的数量,只能由类型 Struct 调用  NumField() int
    
// 返回函数类型的输入参数个数   
NumIn() int
  
// 返回函数类型的返回值个数  
NumOut() int
  
// 返回函数类型的第 i 个值的类型  
Out(i int) Type

复制


2)ValueOf

调用ValueOf函数,实参会转化为interface{ } 类型,存储的是实际变量,提供实际变量的各种信息,返回Value结构体。


Value 结构体定义了很多方法,通过这些方法可以直接操作 Value 字段 ptr 所指向的实际数据:

// 设置切片的 len 字段,如果类型不是切片,就会panic
func (v Value) SetLen(n int)

// 设置切片的 cap 字段
func (v Value) SetCap(n int)

// 设置字典的 kv
func (v Value) SetMapIndex(key, val Value)

// 返回切片、字符串、数组的索引 i 处的值
func (v Value) Index(i int) Value

// 根据名称获取结构体的内部字段值
func (v Value) FieldByName(name string) Value

.....

复制


Value
字段还有很多其他的方法。例如:

// 用来获取 int 类型的值
func (v Value) Int() int64

// 用来获取结构体字段(成员)数量
func (v Value) NumField() int

// 尝试向通道发送数据(不会阻塞)
func (v Value) TrySend(x reflect.Value) bool

// 通过参数列表 in 调用 v 值所代表的函数(或方法
func (v Value) Call(in []Value) (r []Value) 

// 调用变参长度可变的函数
func (v Value) CallSlice(in[]Value) []Value
.....

复制


1


04


反射三大定律


1)反射将接口值转换为反射对象

  type NewInt int
  var x NewInt = 3
  fmt.Println(reflect.ValueOf(x), reflect.TypeOf(x))

复制


2) 反射可以将反射对象转换为接口对象

  type NewInt int
  var x NewInt = 3
  y := reflect.ValueOf(x)
  z := y.Interface()
  var z NewInt = z.(NewInt)

复制


3) 反射对象必须是可修改的

  type NewInt int
  var x NewInt = 3
  //获取指针对应的反射对象 v (必须使用指针修改指向的值)
  y := reflect.ValueOf(&x) 
  y = y.Elem()
  y.SetInt(11)
  fmt.Println(y.Interface())

复制


1


05


Golang reflect慢主要有两个原因


    1)涉及到内存分配以及后续的GC;

    2)reflect实现里面有大量的枚举,也就是for循环,比如类型之类的。


例子:

 // 通过接口来获取任意参数,然后一一揭晓
 func DoFiledAndMethod(input interface{}) {
     getType := reflect.TypeOf(input)
     fmt.Println("get Type is :", getType.Name())

getValue := reflect.ValueOf(input)
     fmt.Println("get all Fields is:", getValue)


     // 获取方法字段
     // 1. 先获取interface的reflect.Type,然后通过NumField进行遍历
     // 2. 再通过reflect.Type的Field获取其Field
     // 3. 最后通过Field的Interface()得到对应的value
     for i := 0; i < getType.NumField(); i++ {
           field := getType.Field(i)
           value := getValue.Field(i).Interface()
           fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
     }


     // 获取方法
     // 1. 先获取interface的reflect.Type,然后通过.NumMethod进行遍历
     for i := 0; i < getType.NumMethod(); i++ {
            m := getType.Method(i)
            fmt.Printf("%s: %v\n", m.Name, m.Type)
      }
}


      // 运行结果:
      get Type is : User
      get all Fields is: {1 Allen.Wu 25}
      Id: int = 1
      Name: string = Allen.Wu
      Age: int = 25
      ReflectCallFunc: func(main.User)


复制

1


06


反射的实际应用

  IDE 中的代码自动补全功能、对象序列化(json 函数库)、fmt 相关函数的实现、ORM(全称是:Object Relational Mapping,对象关系映射)……

    

文章转载自欢乐毅城,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论