• Go中的反射reflect


    前面我们在学习到struct结构体的时候,因为结构体中的字段首字母大写,而我们想把json文件映射到该结构体上时,需要在在结构体字段后面加上json标签,表明结构体字段和json字段的映射关系。这其中就用到了反射的方式去获取标签,取出该标签对应的json字段然后存储到结构体字段上。

    Go语言中提供了反射的包为reflect。在 reflect 包中,主要通过两个函数TypeOf()ValueOf()实现反射,TypeOf()获取到的结果是reflect.Type 类型,ValueOf()获取到的结果是reflect.Value类型。

    1. 理解反射的类型(Type)

    reflect.TypeOf()返回的是Type类型,Type中包含了一个对象会有的相关信息,对象名,对象类型,对象的方法,对象中的属性等等。

    reflect.Type中的方法:

    // 通用方法
    
    func (t *rtype) String() string // 获取 t 类型的字符串描述,不要通过 String 来判断两种类型是否一致。
    
    func (t *rtype) Name() string // 获取 t 类型在其包中定义的名称,未命名类型则返回空字符串。
    
    func (t *rtype) PkgPath() string // 获取 t 类型所在包的名称,未命名类型则返回空字符串。
    
    func (t *rtype) Kind() reflect.Kind // 获取 t 类型的类别。
    
    func (t *rtype) Size() uintptr // 获取 t 类型的值在分配内存时的大小,功能和 unsafe.SizeOf 一样。
    
    func (t *rtype) Align() int  // 获取 t 类型的值在分配内存时的字节对齐值。
    
    func (t *rtype) FieldAlign() int  // 获取 t 类型的值作为结构体字段时的字节对齐值。
    
    func (t *rtype) NumMethod() int  // 获取 t 类型的方法数量。
    
    func (t *rtype) NumField() int //返回一个struct 类型 的属性个数,如果非struct类型会抛异常
    
    func (t *rtype) Method() reflect.Method  // 根据索引获取 t 类型的方法,如果方法不存在,则 panic。
    // 如果 t 是一个实际的类型,则返回值的 Type 和 Func 字段会列出接收者。
    // 如果 t 只是一个接口,则返回值的 Type 不列出接收者,Func 为空值。
    
    func (t *rtype) MethodByName(string) (reflect.Method, bool) // 根据名称获取 t 类型的方法。
    
    func (t *rtype) Implements(u reflect.Type) bool // 判断 t 类型是否实现了 u 接口。
    
    func (t *rtype) ConvertibleTo(u reflect.Type) bool // 判断 t 类型的值可否转换为 u 类型。
    
    func (t *rtype) AssignableTo(u reflect.Type) bool // 判断 t 类型的值可否赋值给 u 类型。
    
    func (t *rtype) Comparable() bool // 判断 t 类型的值可否进行比较操作
    //注意对于:数组、切片、映射、通道、指针、接口 
    func (t *rtype) Elem() reflect.Type // 获取元素类型、获取指针所指对象类型,获取接口的动态类型
    

    有个方法是Elem(),获取元素类型、获取指针所指对象类型,获取接口的动态类型。对指针类型进行反射的时候,可以通过reflect.Elem()获取这个指针指向元素的类型。

    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func (User) GetUser(user User) string{
    	return user.Name + " " + strconv.Itoa(user.Age)
    }
    
    func main() {
    	user := &User{"xiaoming", 13}
    	of := reflect.TypeOf(user)
    	elem := reflect.TypeOf(user).Elem()
    	fmt.Println(of.Kind(),of.Name())
    	fmt.Println(elem.Kind(),elem.Name())
    }
    
    结果:
    ptr 
    struct User
    

    从上面的结果来看,TypeOf(user)的种类为ptr,Go中反射对所有的指针变量的种类都是ptr,但是指针变量的类型名称是空的。

    通过Elem()方法可以得到指针指向的元素的类型和名称,得到User的类型为struct,名称为User。

    1.1 通过反射获取结构体成员类型
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func (User) GetUser(user User) string{
    	return user.Name + " " + strconv.Itoa(user.Age)
    }
    
    
    func main() {
    	user := User{"xiaoming", 13}
    
    	//反射获取对象类型,字段类型
    	userType := reflect.TypeOf(user)
    	fmt.Println(userType.Name(),userType.Kind())
    
    	for i := 0; i < userType.NumField(); i++ {
    		fieldType := userType.Field(i)
    		fmt.Printf("name: %v  tag: '%v'
    ", fieldType.Name, fieldType.Tag)
    	}
    	
    	if name, ok := userType.FieldByName("Name"); ok {
    		fmt.Println(name)
    	}
    }
    
    
    输出信息:
    User struct
    name: Name  tag: ''
    name: Age  tag: ''
    {Name  string  0 [0] false}
    

    在上面的代码中,NumField()方法获取结构体类型中的属性个数,通过Field(i)方法来获取结构体中的属性,返回的是StructField类型的结构体。该对象中包含如下信息:

    type StructField struct {
        Name string          // 字段名
        PkgPath string       // 字段路径
        Type      Type       // 字段反射类型对象
        Tag       StructTag  // 字段的结构体标签
        Offset    uintptr    // 字段在结构体中的相对偏移
        Index     []int      // Type.FieldByIndex中的返回的索引值
        Anonymous bool       // 是否为匿名字段
    }
    

    可以看到有我们关注的字段名,字段类型,还有tag类型等等。

    2. 通过反射获取对象值

    获取对象值通过ValueOf()方法来实现。

    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func (User) GetUser(user User) string{
    	return user.Name + " " + strconv.Itoa(user.Age)
    }
    
    func main() {
    	user := User{"xiaoming", 13}
    	
    	//反射获取对象值
    	elem := reflect.ValueOf(user)
    	fmt.Println(elem)
    	for i := 0; i < elem.NumField(); i++ {
    		field := elem.Field(i)
    		i2 := field.Type()
    		fmt.Println(i2.Name(), " ", field)
    	}
    }
    
    输出:
    {xiaoming 13}
    string   xiaoming
    int   13
    
    
    2.1 创建对象和调用方法
    package main
    
    import (
    	"fmt"
    	"reflect"
    	"strconv"
    )
    
    type User struct {
    	Name string
    	Age  int
    }
    
    func (User) GetUser(user User) string{
    	return user.Name + " " + strconv.Itoa(user.Age)
    }
    
    func main() {
    	user1 := User{"xiaoming", 13}
    	user := User{}
    
    
    	//反射获取对象值
    	elem := reflect.TypeOf(user)
    	//创建一个实例
    	value:= reflect.New(elem).Elem()
    	value.Field(0).SetString("xiaohong")
    	value.Field(1).SetInt(15)
    
    	fmt.Println(value.Field(0),value.Field(1))
    
    	of := reflect.ValueOf(user)
    	params := make([]reflect.Value,1)
    	params[0] = reflect.ValueOf(user1)
    	//调用方法,传递参数
    	call := of.Method(0).Call(params)
    	fmt.Println(call)
    }
    
    输出:
    
    xiaohong 15
    [xiaoming 13]
    
  • 相关阅读:
    AIX上解压缩.tar.Z, .tar.gz, .zip及.tgz
    linux用rdate命令实现同步时间
    ssh-copy-id:/usr/bin/ssh-copy-id: ERROR: No identities found
    开源权限管理-源码介绍
    C#设计模式学习资料--装饰模式
    C#设计模式学习资料--外观模式
    C#设计模式学习资料--原型模式
    C#设计模式学习资料--创建者模式
    C#设计模式学习资料--观察者模式
    C#设计模式学习资料--模版方式模式
  • 原文地址:https://www.cnblogs.com/rickiyang/p/11074172.html
Copyright © 2020-2023  润新知