Golang 反射

Jackey Golang 1,141 次浏览 , 1条评论

什么是反射

在运行期间(不是编译期间)探知对象的类型信息和内存结构、更新变量、调用他们的方法。

如何使用反射

  • 函数的参数类型是interface{},需要在运行时对原始类型进行判断,针对不同的类型采取不同的处理方式。比如json.Marshal(v interface{})
  • 在运行时,根据某些条件动态决定调用哪个函数,比如根据配置文件执行响应的算子函数

使用反射的列子

type User struct {
    Name string
    Age int
    Sex byte `json:"gender"`
}

func main()  {
    user := User{
        Name: "Jackey",
        Age:  30,
        Sex:  1,
    }
    r, _ := json.Marshal(user)
    fmt.Println(string(r))
}

运行结果:

{"Name":"Jackey","Age":30,"gender":1}

反射的弊端

  1. 代码难以阅读,难以维护
  2. 编译期间不能发现类型错误,覆盖测试难度大,有些bug需要到线上运行很长时间才能发现,可能会造成严重后果
  3. 反射性能很差,通畅比正常代码慢一到两个数量级。在对性能要求很高,或大量反复代用的代码块里不建议使用反射。

反射的基础数据类型

type Type interface {
    Method(int) Method                           // 第i个方法
    MethodByName(string) (Method, bool)          // 根据名称获取方法
    NumMethod() int                              // 方法的个数
    Name() string                                // 获取结构体名称
    PkgPath() string                             // 包路径
    Size() uintptr                               // 占用内存大小
    String() string                              // 获取字符串表述
    Kind() Kind                                  // 数据类型
    Implements(u Type) bool                      // 判断是否实现了某接口
    AssignableTo(u Type) bool                    // 能否赋给另外一种类型
    ConvertibleTo(u Type) bool                   // 能否转换为另外一种类型
    Elem() Type                                  // 解析指针
    Field(i int) StructField                     // 第i个成员
    FieldByIndex(index []int) StructField        // 根据index路径获取嵌套成员
    FieldByName(name string) (StructField, bool) // 根据名称获取成员
    Len() int                                    // 容器的长度
    NumIn() int                                  // 输出参数的个数
    NumOut() int                                 // 返回参数的个数
}

reflect.Value

type Value struct {
    // 代表的数据类型
    typ *rtype
    // 指向原始总数据的指针
    ptr unsafe.Pointer
}

通过reflect.Value可以获取、修改原始数据类型里的值

获取Type类型

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

func main() {
    // 通过TypeOf()得到Type类型
    user := reflect.TypeOf(&User{})
    fmt.Println(user)               // *User
    fmt.Println(user.Elem())        // User, Elem() 对指针类型进行解析
    fmt.Println(user.Kind())        // ptr
    fmt.Println(user.Elem().Kind()) // struct
}

获取Field信息

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

func main() {
    // 通过TypeOf()得到Type类型
    user := reflect.TypeOf(User{})
    for i := 0; i < user.NumField(); i++ {
        field := user.Field(i)
        fmt.Printf("%s offset %d anonymous %t type %s exported %t json tag %s \n",
            field.Name,            // 变量名称
            field.Offset,          // 相对于结构体首地址的内存偏移量,string类型会占据16个字节
            field.Anonymous,       // 是否为匿名成员
            field.Type,            // 数据类型, reflect.Type 类型
            field.IsExported(),    // 包外是否可见(即是否以大写字母开头)
            field.Tag.Get("json")) // 获取成员变量后面``里面定义的tag
    }
}

输出结果:

Name offset 0 anonymous false type string exported true json tag  
Age offset 16 anonymous false type int exported true json tag  
Sex offset 24 anonymous false type uint8 exported true json tag gender

获取method信息

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

func (u *User) GetName() string {
    return u.Name
}

func main() {
    // 通过TypeOf()得到Type类型
    user := reflect.TypeOf(&User{})
    // user.NumMethod() 成员方法的个数,接收值为非指针(&User{})的方法不包含在内
    for i := 0; i < user.NumMethod(); i++ {
        method := user.Method(i)
        fmt.Printf("method name:%s, type:%s, exported:%s \n",
            method.Name, method.Type, method.IsExported()) // 获取成员变量后面``里面定义的tag
    }
}

输出内容:

method name:GetName, type:func(*main.User) string, exported:%!s(bool=true)

获取函数信息

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

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

func main() {
    // 通过TypeOf()得到Type类型
    u := &User{}
    typeFunc := reflect.TypeOf(u.Add)
    // user.NumMethod() 成员方法的个数,接收值为非指针(&User{})的方法不包含在内
    for i := 0; i < typeFunc.NumIn(); i++ {
        argTyp := typeFunc.In(i)
        fmt.Printf("第%d个输入参数的类型%s \n",
            i, argTyp) // 获取成员变量后面``里面定义的tag
    }
}

输出内容:

第0个输入参数的类型int 
第1个输入参数的类型int

赋值和转换关系

  • type1.AssignableTo(type2) // type1代表的类型是否可以赋值给type2代表的类型
  • type1.ConvertibleTo(type2) // type1代表的烈性是否可以转换成type2代表的类型
  • java的反射可以获取继承关系,而go语言不支持继承,所以必须是相同的类型才能AssignableTo和ConvertibleTo
type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

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

type Student struct {
    User
}

func main() {
    userType := reflect.TypeOf(User{})
    userType2 := reflect.TypeOf(User{})
    studentType := reflect.TypeOf(Student{})

    fmt.Println(userType.AssignableTo(studentType))
    fmt.Println(studentType.AssignableTo(userType))
    fmt.Println(userType.ConvertibleTo(studentType))
    fmt.Println(studentType.ConvertibleTo(userType))
    fmt.Println(userType.AssignableTo(userType2))
    fmt.Println(userType.ConvertibleTo(userType2))
}

输出结果:

false
false
false
false
true
true

是否实现接口

  • typeOfPeople := reflect.TypeOf((*People)(nil)).Elem() // 通过reflect.TypeOf((*<interface>)(nil)).Elem 获得接口类型
  • userType := reflect.TypeOf(&User{})
  • userType.Implements(typeOfPeople) // 判断User的指针类型是否实现了People接口
  • User的值类型实现了接口,则指针类型也实现了接口;反过来不行
type People interface {
    Add(a, b int) int
}

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

type Student struct {
    User
}

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

func main() {
    peopleType := reflect.TypeOf((*People)(nil)).Elem()
    userType := reflect.TypeOf(User{})
    studentType := reflect.TypeOf(Student{})
    studentType2 := reflect.TypeOf(&Student{})

    fmt.Println(userType.Implements(peopleType))
    fmt.Println(studentType.Implements(peopleType))
    fmt.Println(studentType2.Implements(peopleType))
}

输出信息:

false
false
true

reflect.Value

func main() {
    userValue := reflect.ValueOf(User{
        Name: "Jackey",
        Age:  30,
        Sex:  0,
    })
    user := userValue.Interface().(User) // 通过Interface()函数把Value转为interface{},再从interface{}强制类型转换,转为原始数据类型
    j, _ := json.Marshal(user)
    fmt.Println(string(j))
}

空Value

func main() {
    var i interface{}
    v := reflect.ValueOf(i)
    fmt.Printf("v持有值 %t\n", v.IsValid())
    var user *User = nil
    v = reflect.ValueOf(user) // Value指向一个nil
    fmt.Printf("v持有的值是nil %t\n", v.IsNil())
    var u User // 只声明,里面的值都是默认值
    v = reflect.ValueOf(u)
    fmt.Printf("v持有的值是对应类型的默认值 %t\n", v.IsZero())
}

输出结果:

v持有值 false
v持有的值是nil true
v持有的值是对应类型的默认值 true

Value转为Type

userType := userValue.Type()

userType.Kind() == userValue.Kind() == reflect.Struct

代表指针的Value

userPtrValue := reflect.ValueOf(&User{})

userValue := userPtrValue.Elem()

userPtrValue := userValue.Addr()

user := userValue.Interface().(User)

userPtr := userPtrValue.Interface().(*User)

通过反射修改struct

func main() {
    var s string = "hello"
    valueS := reflect.ValueOf(&s)     // 必须传指针才能修改数据
    valueS.Elem().SetString("Jackey") // 需要先调Elem()把指针Value转为非指针Value
    fmt.Println(s)

    user := User{}
    valueUser := reflect.ValueOf(&user)
    addrValue := valueUser.Elem().FieldByName("Name")
    if addrValue.CanSet() {
        addrValue.SetString("Jackey") // 未导出成员的值不能修改
    }
    j, _ := json.Marshal(user)
    fmt.Println(string(j))
}

输出结果:

Jackey
{"Name":"Jackey","Age":0,"gender":0}

通过反射修改slice

users := make([]*User, 1, 5)
users[0] = &User{
    Name: "Jackey",
}
sliceVaule := reflect.ValueOf(&users) // 准备通过Value修改users,所以传指针
sliceVaule.Elem().Index(0).Elem().FieldByName("Name").SetString("iJackey")

sliceVaule.Elem().SetLen(2)
// 调用reflect.Value的Set()函数修改其底层指向的原始数据
sliceVaule.Elem().Index(1).Set(reflect.ValueOf(&User{
    Name: "Jackey",
}))
r, _ := json.Marshal(users)
fmt.Println(string(r))

输出结果:

[{"Name":"iJackey","Age":0,"gender":0},{"Name":"Jackey","Age":0,"gender":0}]

通过反射修改map

u1 := &User{
    Name: "Jackey",
    Age:  8,
}
u2 := &User{
    Name: "iJackey",
    Age:  9,
}
userMap := make(map[string]*User, 5)
userMap[u1.Name] = u1
mapVaule := reflect.ValueOf(&userMap)                                      // 注意传指针
mapVaule.Elem().SetMapIndex(reflect.ValueOf(u2.Name), reflect.ValueOf(u2)) // SetMapIndex 往map里添加一个key-value对
r, _ := json.Marshal(userMap)
fmt.Println(string(r))

输出结果:

{"Jackey":{"Name":"Jackey","Age":8,"gender":0},"iJackey":{"Name":"iJackey","Age":9,"gender":0}}

通过反射调用函数

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

func main() {
    valueFunc := reflect.ValueOf(Add)
    args := []reflect.Value{reflect.ValueOf(3), reflect.ValueOf(5)}
    results := valueFunc.Call(args) // 函数返回是一个列表
    sum := results[0].Interface().(int)
    fmt.Println(sum)
}

通过反射调用方法

type User struct {
    Name string
    Age  int
    Sex  byte `json:"gender"`
}

type Student struct {
    User
}

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

func main() {
    student := Student{}
    valueStudent := reflect.ValueOf(&student)                                              // 必须传指针,因为Add()在定义的时候它是指针的方法
    addMethod := valueStudent.MethodByName("Add")                                          // MethodByName()通过Name返回类的成员变量
    resultValue := addMethod.Call([]reflect.Value{reflect.ValueOf(3), reflect.ValueOf(5)}) // 如果无需传参,传入一个空的切片[]reflect.Value{}
    result := resultValue[0].Interface().(int)
    fmt.Println(result)
}

根据反射创建struct

t := reflect.TypeOf(User{})
value := reflect.New(t) // 根据reflect.Type 创建一个对象,得到该对象的指针,再根据指针提到reflect.Value
value.Elem().FieldByName("Name").SetString("Jackey")
user := value.Interface().(*User) // 把发射类型转成go原始类型
r, _ := json.Marshal(user)
fmt.Println(string(r))

输出结果:

{"Name":"Jackey","Age":0,"gender":0}

根据反射创建slice

var slice []User
sliceType := reflect.TypeOf(slice)
sliceValue := reflect.MakeSlice(sliceType, 1, 3)
sliceValue.Index(0).Set(reflect.ValueOf(User{
    Name: "Jackey",
}))
users := sliceValue.Interface().([]User)
r, _ := json.Marshal(users)
fmt.Println(string(r))

输出结果:

[{"Name":"Jackey","Age":0,"gender":0}]

根据反射创建map

var userMap map[string]*User
mapType := reflect.TypeOf(userMap)
mapValue := reflect.MakeMapWithSize(mapType, 10)
user := &User{Name: "Jackey"}
key := reflect.ValueOf(user.Name)
mapValue.SetMapIndex(key, reflect.ValueOf(user)) //SetMapIndex 往map里添加一个key-value对
userMap = mapValue.Interface().(map[string]*User)
r, _ := json.Marshal(userMap)
fmt.Println(string(r))

输出结果:

{"Jackey":{"Name":"Jackey","Age":0,"gender":0}}

 

一条评论

  1. 淄博漏水检测 2022年7月9日 上午9:13 回复

    感谢分享,赞一个

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Go