Loading

Go标准库-reflect学习

reflect包实现了运行时反射,允许程序操作任意类型的对象。 reflect配合interface{}使用,为go增加了动态的特性。

反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息

一、两个重要类型:Type & Value

  调用TypeOf函数获取其动态类型信息,返回一个Type类型值。调用ValueOf函数获取该变量运行时的数据,返回一个Value类型值。

  func TypeOf(i interface{}) Type

  

 1 // 1. 普通类型    
 2     var temp int
 3     typeA := reflect.TypeOf(temp)
 4     fmt.Println(typeA)         //int
 5     fmt.Println(typeA.Name())  //int,返回表示类型名称的字符串
 6     fmt.Println(typeA.Kind())  //int,返回 reflect.Kind 类型的常量
 7 // 2. 指针类型    
 8     type Repo struct{
 9         name string `json:"omitempty"`
10         volume int
11     }
12 
13     //创建一个Student实例指针
14     res := &Repo{"repo1", 10000}
15     typeRes := reflect.TypeOf(res)
16 
17     fmt.Println(typeRes.Name()) //""
18     fmt.Println(typeRes.Kind()) //ptr
19 
20     //获取该指针的元素 [ValueOf 和Value.Elem的区别]
21     resElem := typeRes.Elem()
22 
23     fmt.Println(resElem.Name()) //Repo
24     fmt.Println(resElem.Kind()) //struc
25 // 3. 结构体类型(NumField,Field & tag)
26     t := Repo{"repo2", 20000} // 传入指针类型,编译报错:panic: reflect: NumField of non-struct type
27     for i :=0; i < t.NumField(); i++ {
28         fieldType := t.Field(i) // 获取指定索引结构成员的信息
29                           // t.FieldByName("name") 通过字段名,找到字段的类型信息
30         fmt.Println(fieldType.Name, fieldType.Tag) // name, json:"omit-empty"
31         fmt.Println(fieldType.Tag.Get("json") // omit-empty 使用Get()获取标签的value
32     }
View Code

 

  func ValueOf(i interface{}) Value   [不仅能和上面一样获取值的类型(Type)信息,还可以动态地获取或设置变量的值]

 1 // 1.普通用法
 2     a := 1 //声明并赋值得到一个int类型的a
 3 
 4     //获取其的反射值对象
 5     valueOfA := reflect.ValueOf(a)
 6     fmt.Println(valueOfA) //1
 7     //获取该值的类型,注意elem.(type)这种方法只能用在switch中
 8     fmt.Println(reflect.TypeOf(valueOfA)) //reflect.Value
 9 
10     //调用Interface()方法获取interface{}类型的值,然后使用类型断言进行转换成int类型
11     changedA1 := valueOfA.Interface().(int)
12     fmt.Println(changedA1) //1
13     fmt.Println(reflect.TypeOf(changedA1)) //int
14 
15     //还有另一种类似的方法,就是调用Int()方法将其先转换成int64类型,然后再转成int类型
16     changedA2 := int(valueOfA.Int())
17     fmt.Println(changedA2) //1
18     fmt.Println(reflect.TypeOf(changedA2))//int
19 // 2.结构体(NumField, Field)
20     type User struct {
21         Id int
22         Name string
23         Age int
24     }
25     user := User{1,"Tom",12}
26     v := reflect.ValueOf(user)
27     for i :=0; i < v.NumField(); i++ {
28         val := v.Field(i).Interface()   // 获取指定结构成员的值
29     }
30     
31     for i :=0; i <v.NumMethod(); i++ {
32         m := v.Method(i) // 获取指定索引的方法的信息
33         fmt.Printf("%6s: %v\n", m.Name, m.Type) // 打印方法的名称,方法声明
34     }
View Code

二、判断反射值

  func (v Value) IsNil() bool     判断值是否为nil,常用于判断指针是否为空  

  func (v Value) IsValid() bool  如果v是Value零值会返回false,此时v除了IsValid、String、Kind之外的方法都会导致panic。绝大多数函数和方法都永远不返回Value零值。

  func (v Value) IsZero() boo   判断v是否是其类型的零值

三、修改反射值

  func (v Value) Elem() Value      Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装,类似于*操作,此时的Value表示的是Value的元素且可以寻址

  func (v Value) SetInt(x int64)    设置v的持有值。如果v的Kind不是Int、Int8、Int16、Int32、Int64之一或者v.CanSet()返回假,会panic。

posted @ 2021-12-20 01:18  是凉城吖  阅读(37)  评论(0编辑  收藏  举报