Golang反射

反射

  • 基本介绍
  1. 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
  2. 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  3. 通过反射,可以修改变量的值,可以调用关联的方法。
  4. 使用反射,需要 import (“reflect”)
  • 应用场景
  1. 不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。
  2. 对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。
  • 常用函数和概念
  1. reflect.TypeOf(变量名), 获取变量的类型,返回reflect.Type类型
    //使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
	rTyp := reflect.TypeOf(b) 
	fmt.Println("rType=", rTyp)
  1. reflect.ValueOf(变量名), 获取变量的值,返回reflect.

    Value类型reflect.Value是-一个结构体类型。通过reflect.Value,可以获取到关于该变量的很多信息。

    //使用 reflect.TypeOf() 函数可以获得任意值的类型对象,通过类型对象可以访问任意值的类型信息。
	rTyp := reflect.TypeOf(b) 
	fmt.Println("rType=", rTyp)
  1. 变量interface{} 和 reflect.Value 是可以相互转换的.
    var num int = 100
    rVal := reflect.ValueOf(b)
    //下面我们将 rVal 转成 interface{}
	iV := rVal.Interface()
	//将 interface{} 通过断言转成需要的类型
	num2 := iV.(int)
	fmt.Println("num2=", num2)
  1. reflect.TypeOf.Elem() 获取指针类型的元素类型
type Student struct {
	Name string `json:"username"`
	Age int
}
func main() {
    stu := Student{
		Name : "tom",
		Age : 20,
	}
    rTyp := reflect.TypeOf(stu)

    // 获取指针类型的元素类型
    e := rTyp.Elem()
    // 显示指针变量指向元素的类型名称和种类
    fmt.Printf("name:'%v' kind:'%v'\n", e.Name(), e.Kind()) // name:'User' kind:'struct'
}
  1. 任意值通过 reflect.TypeOf() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的 NumField() 和 Field() 方法获得结构体成员的详细信息。
    type User struct {
        Name   string `json:"username"`
        Age    int
        Salary float64
    }

    func main() {
        user := User{"pd", 18, 9999.99}
        tf := reflect.TypeOf(user)
        // 遍历结构体所有成员
        for i := 0; i < tf.NumField(); i++ {
            // 获取每个成员的结构体字段类型
            fieldType := tf.Field(i)
            fmt.Printf("name:'%v' tag:'%v'\n", fieldType.Name, fieldType.Tag)
            // name:'Name' tag:'json:"username"'
            // name:'Age' tag:''
            // name:'Salary' tag:''
        }
        // 通过字段名, 找到字段类型信息
        userType, ok := tf.FieldByName("Name")
        if ok {
            // 从tag中取出需要的tag
            fmt.Println(userType.Tag.Get("json")) // username
        }
    }
  1. 通过反射获取值信息
    func main() {
        // 声明整型变量a并赋初值
        var a int
        a = 666
        // 获取变量a的反射值对象
        vf := reflect.ValueOf(a)
        // 将vf反射值对象以Interface{}类型取出, 通过类型断言转换为int类型
        r1 := vf.Interface().(int)
        // 将vf反射值对象以int64类型取出
        r2 := vf.Int()
        // 强制类型转换为int类型
        r3 := int(r2)
        fmt.Printf("r1值:%v r1类型:%T\n", r1, r1) // r1值:666 r1类型:int
        fmt.Printf("r2值:%v r2类型:%T\n", r2, r2) // r2值:666 r2类型:int64
        fmt.Printf("r3值:%v r3类型:%T\n", r3, r3) // r3值:666 r3类型:int
    }
  1. 通过反射访问结构体成员的值
    type User struct {
        Name   string
        Age    int
        Salary float64
    }

    func main() {
        user := User{"pd", 18, 9999.99}
        vf := reflect.ValueOf(user)
        // 获取字段数量
        fmt.Printf("NumField:%v\n", vf.NumField()) // NumField:3
        // 获取索引为2的字段
        field := vf.Field(2)
        fmt.Println(field.Type()) // float64
        // 根据名字查找字段
        fbn := vf.FieldByName("Name")
        fmt.Println(fbn.Type()) // string
        // 根据索引查找字段
        fbi := vf.FieldByIndex([]int{1})
        fmt.Println(fbi.Type()) // int
    }
  1. 判断反射值的空和有效性
    func main() {
        // *int的空指针
        var a *int
        fmt.Println(reflect.ValueOf(a).IsNil()) // true

        // nil值
        fmt.Println(reflect.ValueOf(nil).IsValid()) // false

        // 实例化一个结构体
        s := struct{}{}
        // 尝试从结构体中查找一个不存在的字段
        fmt.Println(reflect.ValueOf(s).FieldByName("").IsValid()) // false

        // 尝试从结构体中查找一个不存在的方法
        fmt.Println(reflect.ValueOf(s).MethodByName("").IsValid()) // false
    }
  1. 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
    var num int = 10
    //2. 获取到 reflect.Value
	rVal := reflect.ValueOf(num)
	//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
	rVal.Elem().SetInt(20)

    fmt.Println("num=", num) // 20
  1. 通过类型信息创建实例
    func main() {
        var a int
        // 取变量a的反射类型对象
        tf := reflect.TypeOf(a)
        // 根据反射类型对象创建这个类型的实例值,值以 reflect.Value 类型返回
        obj := reflect.New(tf)
        // 输出类型和种类
        fmt.Printf("type:%v kind:%v\n", obj.Type(), obj.Kind()) // type:*int kind:ptr
    }
  1. 通过反射调用函数、方法
// add函数
func add(a, b int) int {
    return a + b
}

func main() {
    // 将函数包装为反射值对象
    vf := reflect.ValueOf(add)
    // 构造函数参数, 传入两个整型值
    paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
    // 反射调用函数
    retList := vf.Call(paramList)
    // 获取第一个返回值, 取整数值
    fmt.Println(retList[0].Int()) // 30
}
  • 注意事项
  1. reflect.Value.Kind,获取变量的类别,返回的是一个常量
  2. Type 和 Kind 的区别

    Type 是类型, Kind 是类别, Type 和 Kind 可能是相同的,也可能是不同的.

    rTyp := reflect.TypeOf(b)
	rVal := reflect.ValueOf(b)
    //(1) rVal.Kind() ==> 
	kind1 := rVal.Kind()
	//(2) rTyp.Kind() ==>
	kind2 := rTyp.Kind()
  1. 通过反射可以在让变量在interface{} 和Reflect.Value之间相互转换.
  2. 使用反射的方式来获取交量的值(并返回对应的类型),要求数据类型匹配
    var num int = 100
	rTyp := reflect.TypeOf(num)
	rVal := reflect.ValueOf(num)
	
	n2 := 2 + rVal.Int()
	//n3 := rVal.Float() //error panic
  1. 通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方法
    var num int = 10
    //2. 获取到 reflect.Value
	rVal := reflect.ValueOf(num)
	//3. Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
	rVal.Elem().SetInt(20)

    fmt.Println("num=", num) // 20
  1. reflect.Value.Elem() 用于获取指针指向变量
    type User struct {
        Name   string
        Age    int
        Salary float64
    }
    func main() {
        // 声明一个空结构体
        type User struct {}
        // 创建User的实例
        user := &User{}
        // 获取结构体实例的反射类型对象
        t := reflect.TypeOf(user)
        // 显示反射类型对象的名称和种类
        fmt.Printf("name:'%v' kind:'%v'\n", t.Name(), t.Kind()) // name:'' kind:'ptr'
        // 获取指针类型的元素类型
        e := t.Elem()
        // 显示指针变量指向元素的类型名称和种类
        fmt.Printf("name:'%v' kind:'%v'\n", e.Name(), e.Kind()) // name:'User' kind:'struct'
    }
posted @ 2020-09-03 14:19  养诚  阅读(313)  评论(0编辑  收藏  举报