首页 > 技术文章 > golang 反射

finghi 2022-05-29 16:43 原文

 

reflect包

在Go语言的反射机制中,任何接口值都由是一个具体类型具体类型的值两部分组成的(我们在上一篇接口的博客中有介绍相关概念)。 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Typereflect.Value两部分组成,并且reflect包提供了reflect.TypeOfreflect.ValueOf两个函数来获取任意对象的Value和Type。

在反射中关于类型还划分为两种:类型(Type)种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。

func reflectType(e  interface {}) {
    tpe := reflect.TypeOf(e)
    fmt.Println( "type:" , tpe)
    fmt.Printf( "name:%v  kind:%v \n" , tpe.Name(), tpe.Kind())   
}
  func main(){
    k := 1204.58 reflectType(k)  //type: float64 //name:float64  kind:float64 g := "zhansan" reflectType(g)  //type: string //name:string  kind:string }

Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回

reflect包中定义的Kind类型如下:

 1 type Kind uint
 2 const (
 3     Invalid Kind = iota  // 非法类型
 4     Bool                 // 布尔型
 5     Int                  // 有符号整型
 6     Int8                 // 有符号8位整型
 7     Int16                // 有符号16位整型
 8     Int32                // 有符号32位整型
 9     Int64                // 有符号64位整型
10     Uint                 // 无符号整型
11     Uint8                // 无符号8位整型
12     Uint16               // 无符号16位整型
13     Uint32               // 无符号32位整型
14     Uint64               // 无符号64位整型
15     Uintptr              // 指针
16     Float32              // 单精度浮点数
17     Float64              // 双精度浮点数
18     Complex64            // 64位复数类型
19     Complex128           // 128位复数类型
20     Array                // 数组
21     Chan                 // 通道
22     Func                 // 函数
23     Interface            // 接口
24     Map                  // 映射
25     Ptr                  // 指针
26     Slice                // 切片
27     String               // 字符串
28     Struct               // 结构体
29     UnsafePointer        // 底层指针
30 )

通过 reflect.ValueOf 获取值

func getreflect(e interface{}) {
    tpe := reflect.ValueOf(e)
    k := tpe.Kind()
    switch k {
    case reflect.String:
        fmt.Printf("value:%v\n", string(tpe.String()))
    case reflect.Int:
        fmt.Printf("value:%v\n", int(tpe.Int()))
    case reflect.Float64:
        fmt.Printf("value:%v\n", float64(tpe.Float()))
    case reflect.Bool:
        fmt.Printf("value:%v\n", bool(tpe.Bool()))
    }
}

想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。

func setvalue(e interface{}) {
    k := reflect.ValueOf(e)
    // 反射中使用 Elem()方法获取指针对应的值
    if k.Elem().Kind() == reflect.Float64 {
        k.Elem().SetFloat(float64(200.5))
    }
}
func main(){
  k := 1204.58
  fmt.Println(k) // 1204.58
  setvalue(&k)
  fmt.Println(k) //  200.5
}

isNil()和isValid()

func (v Value) IsNil() bool

IsNil()报告v持有的值是否为nil。v持有的值的分类必须是通道、函数、接口、映射、指针、切片之一;否则IsNil函数会导致panic。


func (v Value) IsValid() bool

IsValid()返回v是否持有一个值。如果v是Value零值会返回假,此时v除了IsValid、String、Kind之外的方法都会导致panic。

func main() {
	// *int类型空指针
	var a *int
	fmt.Println("var a *int IsNil:", reflect.ValueOf(a).IsNil())
	// nil值
	fmt.Println("nil IsValid:", reflect.ValueOf(nil).IsValid())
	// 实例化一个匿名结构体
	b := struct{}{}
	// 尝试从结构体中查找"abc"字段
	fmt.Println("不存在的结构体成员:", reflect.ValueOf(b).FieldByName("abc").IsValid())
	// 尝试从结构体中查找"abc"方法
	fmt.Println("不存在的结构体方法:", reflect.ValueOf(b).MethodByName("abc").IsValid())
	// map
	c := map[string]int{}
	// 尝试从map中查找一个不存在的键
	fmt.Println("map中不存在的键:", reflect.ValueOf(c).MapIndex(reflect.ValueOf("娜扎")).IsValid())
}

结构体反射

任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()Field()方法获得结构体成员的详细信息。

reflect.Type中与获取结构体成员相关的的方法如下表所示。

方法 说明
Field(i int) StructField 根据索引,返回索引对应的结构体字段的信息。
NumField() int 返回结构体成员字段数量。
FieldByName(name string) (StructField, bool) 根据给定字符串返回字符串对应的结构体字段的信息。
FieldByIndex(index []int) StructField 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息。
FieldByNameFunc(match func(string) bool) (StructField,bool) 根据传入的匹配函数匹配需要的字段。
NumMethod() int 返回该类型的方法集中方法的数目
Method(int) Method 返回该类型方法集中的第i个方法
MethodByName(string)(Method, bool) 根据方法名返回该类型方法集中的方法

StructField的定义如下:

type StructField struct {
    // Name是字段的名字。PkgPath是非导出字段的包路径,对导出字段该字段为""。
    // 参见http://golang.org/ref/spec#Uniqueness_of_identifiers
    Name    string
    PkgPath string
    Type      Type      // 字段的类型
    Tag       StructTag // 字段的标签
    Offset    uintptr   // 字段在结构体中的字节偏移量
    Index     []int     // 用于Type.FieldByIndex时的索引切片
    Anonymous bool      // 是否匿名字段
}

实战 :

type User struct { //创建一个user 类型的结构体
	// gorm.Model
	Name     string `json:"name"`
	Age      int    `json:"age"`
	Username string `json:"username"`
	Password string `json:"password"`
	Gender   string `json:"gender"`
}

func (user User) Setmyname() {
	fmt.Println("我的名字叫", user.Name)
}
func (user User) Setmyage() {
	fmt.Println("我的名字叫", user.Age)
}

 

func reflectType(e interface{}) {
	tpe := reflect.TypeOf(e)
	for i := 0; i < tpe.NumField(); i++ {
		field := tpe.Field(i)
		fmt.Printf("name:%v index:%d type:%v tag:%v\n", field.Name, field.Index[i], field.Type, field.Tag.Get("json"))
	}
}
func main(){
    user := User{
		Name:     "zhansan",
		Age:      25,
		Username: "root",
		Password: "admin",
		Gender:   "nan",
	}
	reflectType(user)
    //name:Name index:[0] type:8 tag:name
    //name:Age index:[1] type:8 tag:age
    //name:Username index:[2] type:8 tag:username
    //name:Password index:[3] type:8 tag:password
    //name:Gender index:[4] type:8 tag:gender
}

    v := reflect.ValueOf(user)
	t := reflect.TypeOf(user)
	
	for index := 0; index < v.NumMethod(); index++ {
		methodType := v.Method(index).Type()
		fmt.Printf("method name:%s\n", t.Method(index).Name)
		fmt.Printf("method:%s\n", methodType)
		var ref = []reflect.Value{}
		v.Method(index).Call(ref)
	}

 

推荐阅读