• Go 反射


         文章转载地址:https://www.flysnow.org/2017/06/13/go-in-action-go-reflect.html

    1. TypeOf 和 ValueOf 

         在 Go 的反射定义中,任何接口都由两部分组成,一个是接口的具体类型,一个是具体类型对应的值。比如:

    var i int = 3,因为 interface{} 可以表示任何类型,所以变量 i 可以转换成 interface{} ,所以可以把变量 i 当成一个

    接口,那么这个变量在 Go 反射中的表示就是 <type,value> ,其中 value 为变量的值 3,type 为类型 int

         在 Go 反射中,标准库为我们提供两种类型来分别表示他们 reflect.Value 和 reflect.Type ,并提供两个函数来获

    取任意对象的 Value 和 Type,看如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个 User 结构体
    type User struct {
    	Name string
    	Age int
    }
    
    func main() {
    	u := User{"张三",25}
    	t := reflect.TypeOf(u)
    	fmt.Println(t)
    }
    
    -------------------------------
    
    输出结果:
    
    main.User
    

      reflect.TypeOf 可以获取任意对象的具体类型,通过上面的例子可以看到打印输出的结果是 main.User 这个结构体类型

           那么如何反射获取一个对象的 Value:

    v := reflect.ValueOf(u)
    fmt.Println(v)
    
    --------------------------
    
    输出结果:
    
    {张三 25}
    

      对于以上两种输出,Go 语言还通过 fmt.Printf 函数提供了更为简便的方法

    fmt.Printf("%T
    ",u)
    fmt.Printf("%v
    ",u)
    

    2.reflect.Value 转原始类型

       上面的例子中我们通过 reflect.ValueOf 函数把任意类型对象转换成 reflect.Value 类型,如果我们想逆向转换回来呢?其实也

    是可以的,reflect.Value 为我们提供了 interface 方法,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个 User 结构体
    type User struct {
    	Name string
    	Age int
    }
    
    func main() {
    	u := User{"张三",25}
    	// 使用 refelct.ValueOf 转换成 refelct.Value
    	v := reflect.ValueOf(u)
    	fmt.Println(reflect.TypeOf(v))
    
    	// 使用 interface 方法转换成 main.User
    	u1 := v.Interface().(User)
    	fmt.Println(reflect.TypeOf(u1))
    }
    
    -----------------------------------------------------
    
    输出结果:
    
    reflect.Value
    main.User
    

    3.获取类型底层类型

      底层类型是什么意思?其实对应的主要是基础类型、接口、结构体、指针这些,因为我们可以通过 Type 关键字声明

    很多新的类型,比如上面的例子,对象 u 的实际类型是 User,但对应的底层类型是 struct 这个结构体类型,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个 User 结构体
    type User struct {
    	Name string
    	Age int
    }
    
    func main() {
    	u := User{"张三",25}
    	t := reflect.TypeOf(u)
    	fmt.Println(t.Kind())
    }
    
    --------------------------------
    
    输出结果:
    
    struct
    

    4.遍历字段和方法

       通过反射,我们可以获取一个结构体类型的字段,也可以获取一个类型的导出方法,这样我们就可以在运行时了解一个

    类型的结构,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个 User 结构体
    type User struct {
    	Name string
    	Age int
    }
    
    // 给结构体 User 绑定一个方法
    func (u User) Print() {
    	fmt.Println("I am User")
    }
    
    func main() {
    	u := User{"张三",26}
    	t := reflect.TypeOf(u)
    
    	// 获取结构体类型的字段
    	for i := 0; i < t.NumField(); i++  {
    		fmt.Println(t.Field(i).Name)
    	}
    
    	// 获取结构体类型的方法
    	for i := 0; i < t.NumMethod(); i++  {
    		fmt.Println(t.Method(i).Name)
    	}
    }
    
    ---------------------------------------------------
    
    输出结果:
    
    Name
    Age
    Print
    

    5.修改字段的值

      我们还可以通过反射修改某个字段的值,如下示例(使用反射修改变量的值):

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main() {
    	x := 2
    	v := reflect.ValueOf(&x)
    	v.Elem().SetInt(100)
    	fmt.Println(x)
    }
    
    ---------------------------------
    
    输出结果:
    
    100
    

       因为 reflect.ValueOf 返回的是一份值的拷贝,所以要想修改一个变量的值前提是要传入修改变量的地址。

    其次需要我们调用 Elem 方法找到指针指向的值。最后我们就可以使用 SetInt 方法修改值了 

          如何修改结构体字段的值呢?如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个结构体
    type User struct {
    	Name string
    	Age int
    }
    
    func main() {
    	u := User{"tom",11}
    	v := reflect.ValueOf(&u.Name)
    	v.Elem().SetString("paul")
    	fmt.Println(u)
    }
    
    ----------------------------------------
    
    输出结果:
    
    {paul 11}
    

    6.动态调用方法

      结构体的方法不仅可以正常调用,还可以通过反射调用。要想通过反射调用,我们先要获取到需要调用的方法,

    然后进行传参调用,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个结构体
    type User struct {
    	Name string
    	Age int
    }
    
    // 给 User 绑定一个方法
    func (u User) Print(prfix  string) {
    	fmt.Printf("%s:Name is %s,Age is %d",prfix,u.Name,u.Age)
    }
    
    func main() {
    	u := User{"lotus",26}
    	v := reflect.ValueOf(u)
    
    	// MethodByName 根据方法名获取方法对象
    	mPrint := v.MethodByName("Print")
    
    	// 构建参数
    	args := []reflect.Value{reflect.ValueOf("前缀")}
    	fmt.Println(mPrint.Call(args))
    }
    
    --------------------------------------------------------------
    
    输出结果:
    
    前缀:Name is lotus,Age is 26[]
    

      MethodByName 可以根据一个方法名获取一个方法对象,然后我们构建好该方法需要的参数,最后调用

    Call 就达到了动态调用方法的目的

    7 通过反射获取 struct 的 Tag 

       字段的 Tag 是标记到字段上的,所以我们可以通过先获取字段,然后再获取标记在字段上的 Tag,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个 User 结构体
    type User struct {
    	Name string `name`
    	Age int `age`
    }
    
    func main() {
    	var u User
    	t := reflect.TypeOf(u)
    
    	for i := 0; i < t.NumField(); i++ {
    		sf := t.Field(i)
    		fmt.Println(sf.Tag)
    	}
    }
    
    ---------------------------------------------
    
    输出结果:
    
    name
    age
    

      通过 .Tag 就可以获取到对应的 Tag 

    8 字段 Tag 的键值对

       一个 struct 的字段可能对应多个不同的 Tag,以便满足不同的功能场景,在 Go 中,struct 为我们提供了

    键值对的 Tag ,来满足我们针对不同场景的需求,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个结构体
    type User struct {
    	Name string `json:"name"`
    	Age int `json:"age"`
    }
    
    func main() {
    	var u User
    	t := reflect.TypeOf(u)
    
    	for i := 0; i < t.NumField(); i++ {
    		sf := t.Field(i)
    		fmt.Println(sf.Tag.Get("json"))
    	}
    }
    
    --------------------------------------------------
    
    输出结果:
    
    name
    age
    

      上面例子,使用了键值对的方式配置 struct Tag,Key-Value 以冒号分开,这里的 Key 就是 json,所以,我们

    可以通过这个 Key 获取对应的值,也就是通过 .Tag.Get("json") 方法。 Get 方法就是通过一个 Key 获取对应的 tag 设置

           除此之外,我们还可以通过设置多个 key,如下示例:

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    // 定义一个结构体
    type User struct {
    	Name string `json:"name" bson:"b_name"`
    	Age int `json:"age" bson:"b_age"`
    }
    
    func main() {
    	var u User
    	t := reflect.TypeOf(u)
    
    	for i := 0; i < t.NumField(); i++ {
    		sf := t.Field(i)
    		fmt.Println(sf.Tag.Get("json"),",",sf.Tag.Get("bson"))
    	}
    }
    
    --------------------------------------------------------------------------
    
    输出结果:
    
    name , b_name
    age , b_age
    

      多个 Key 之间使用空格分开,然后通过 Get 方法获取不同的 Key 值

  • 相关阅读:
    asp.net webapi 无法使用多个post,浏览器不支持put delete,405错误
    vs2019 nable-migrations : 无法将“enable-migrations”项识别为 cmdlet 使用“1”个参数调用“LoadFrom”时发生异常:“ EntityFramew
    javascript 扩展运算符(spread)三个点(...)的作用及用法
    PIE SDK元素的选择和取消选择
    PIE SDK元素的删除
    PIE SDK元素事件的监听
    PIE SDK临时元素的绘制
    PIE SDK图片元素的绘制
    PIE SDK文本元素的绘制
    PIE SDK面元素的绘制
  • 原文地址:https://www.cnblogs.com/leeyongbard/p/10374299.html
Copyright © 2020-2023  润新知