反射
反射
在运行时反射是程序检查其所拥有的结构,尤其是类型的一种能力;这是元编程的一种形式。它同时也是造成迷惑的来源。反射可以在运行时检查类型和变量,例如它的大小、方法和 动态 的调用这些方法。这对于没有源代码的包尤其有用。
从接口类型变量到反射类型对象
反射可以将接口类型变量转换为反射类型对象,这里的反射类型指的是
// TypeOf返回表示i的动态类型的反射Type。如果i是nil接口值,则TypeOf返回nil。
func TypeOf(i interface{}) Type
// ValueOf返回一个新的Value,初始化为存储在接口i中的具体值。ValueOf(nil)返回零值
func ValueOf(i interface{}) Value
然后我们可以使用
var p int = 10
v1 := reflect.ValueOf(p)//返回Value类型对象,值为10
t1 := reflect.TypeOf(p)//返回Type类型对象,值为int
fmt.Println("v1:",v1)
fmt.Println("t1",t1)
v2 := reflect.ValueOf(&p)//返回Value类型对象,值为&p,变量p的地址
t2 := reflect.TypeOf(&p)//返回Type类型对象,值为*int
fmt.Println("v2:",v2)
fmt.Println("t2:",t2)
/**
v1: 10
t1: int
v2: 0xc4200180b8
t2: *int
**/
其中
将反射类型对象转化为接口类型变量
这里根据一个
func (v Value) Interface() (i interface{})
// 接口将v的当前值作为接口{}返回。它相当于:
var i interface{} = (v's underlying value)
// 如果通过访问未导出的struct字段获得Value,则会发生混乱。
然后我们可以使用
var a int = 10
v1 := reflect.ValueOf(&a) //返回Value类型对象,值为&a,变量a的地址
t1 := reflect.TypeOf(&a) //返回Type类型对象,值为*int
fmt.Println("v1:",v1)
fmt.Println("t1:",t1)
v2 := v1.Interface() //返回空接口变量
v3 := v2.(*int) //类型断言,断定v1中type=*int
fmt.Printf("%T %v\n", v3, v3)
fmt.Println("v3:",*v3)
/**
v1: 0xc420082010
t1: *int
*int 0xc420082010
v3: 10
**/
其实
修改反射类型对象
果要修改反射类型对象,其值必须是“addressable” 在上面第一种反射定律将“接口类型变量”转换为“反射类型对象”我们可以知道,反射对象包含了接口变量中存储的值以及类型。如果反射对象中包含的值是原始值,那么可以通过反射对象修改原始值,如果反射对象中包含的值不是原始值(反射对象包含的是副本值或指向原始值的地址
那么我们可以通过
// CanSet报告是否可以更改v的值.仅当值可寻址且未通过使用未导出的struct字段获取时,才能更改值。如果CanSet返回false,则调用Set或任何特定于类型的setter(例如,SetBool,SetInt)将会发生混乱。
func (v Value) CanSet() bool
注意,
var p float64 = 3.4
v1 := reflect.ValueOf(&p)
if v1.Kind() == reflect.Ptr && !v1.Elem().CanSet() { //判断是否为指针类型,元素是否可以修改
fmt.Println("cannot set value")
return
} else {
v1 = v1.Elem() //实际取得的对象
fmt.Println("CanSet return bool:", v1.CanSet())
}
v1.SetFloat(6.1)
fmt.Println("p=",p)
/**
CanSet return bool: true
p= 6.1
**/
从运行结果看值修改成功了,但是这里出现了
// Elem返回接口v包含的值或指针v指向的值。如果v的Kind不是Interface或Ptr,它会发生恐慌。如果v为nil,则返回零值。
func (v Value) Elem() Value
在这里要修改变量
通过反射可以很容易的修改变量的值,我们首先要通过反射拿到这个字段的地址值类型
因此在反射中使用反射包提供
案例分析
结构体标签
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"name"`
Gender string `json:"gender"`
Age int `json:"age"`
}
func main() {
types := reflect.TypeOf(&User{}).Elem()
value := reflect.ValueOf(&User{}).Elem()
fmt.Println("values Numfield:",value.NumField())
for i:=0;i<types.NumField();i++ {
m := types.Field(i).Tag.Get("json")
fmt.Println(m)
}
}
/**
values Numfield: 3
name
gender
age
**/