go反射机制

1. 反射定律

  • 反射可以将“接口类型变量”转换为“反射类型对象”。
  • 反射可以将“反射类型对象”转换为“接口类型变量”。
  • 如果要修改“反射类型对象”,其值必须是“可写的”。

a. “接口类型变量”=>“反射类型对象”


所谓的反射类型,就是reflect.Type和reflect.Value


	var a int = 30

	v := reflect.ValueOf(a)  //返回Value类型对象,值为30
	t := reflect.TypeOf(a)   //返回Type类型对象,值为int
	fmt.Println(v)
	fmt.Println(t)

	v = reflect.ValueOf(&a)  //返回Value类型对象,值为&a,变量a的地址
	t = reflect.TypeOf(&a)   //返回Type类型对象,值为*int
	fmt.Println(v)
	fmt.Println(t)

上面的案例通过使用reflect.ValueOf和reflect.TypeOf将接口类型变量分别转换为反射类型对象value和type。

  • value中包含了接口中的实际值。
  • type中包含了接口中的实际类型。
大家可能对上面的案例感到疑惑,程序里没有接口类型变量啊,哪来的接口类型变量到反射类型对象的转换啊?
事实上,reflect.ValueOf和reflect.TypeOf的参数类型都是interface{},空接口类型,而返回值的类型是reflect.Value和reflect.Type,中间的转换由reflect包来实现。

b. “反射类型对象”=>“接口类型变量”


基本方法是: reflectValue值.Interface.(要转化为的类型)

根据一个reflect.Value类型(注意没有reflect.Type)的变量,我们可以使用 Interface方法恢复其接口类型的值。事实上,这个方法会把 type 和 value信息打包并填充到一个接口变量中,然后返回。

	var a int = 30
	value := reflect.ValueOf(&a)      //返回Value类型对象,值为&a,变量a的地址
	t := value.Interface().(*int)     //类型断言,断定v1中type=*int
	fmt.Printf("%T %v\n", t, t)   // *int 0xc420086008
	fmt.Println(*t)                      // 30


	cir := 6.28
	value2 := reflect.ValueOf(cir)
	t2 := value2.Interface().(float64)
	fmt.Printf("%T %v\n", t2, t2)  // float64 6.28
	fmt.Println(t2)                  // 6.28


	t3 := value2.Interface().(int) //error : interface conversion: interface {} is float64, not int
	fmt.Println(t3)

最关键的两步

v1 := value.Interface()  // 返回的是一个接口变量
v2 := v1.(float64)       // 再判断这个接口变量是否能转化为某类型变量

c. 修改“反射类型对象”


基本方法: 指针类型的value.Elem().SetFloat(待赋的新值)

注意这里用到的也是`reflect.ValueOf`

	var circle float64 = 6.28
	value := reflect.ValueOf(circle)
	fmt.Println(value.CanSet())           // false
	fmt.Println(value)                    // 6.28
	
	value2 := reflect.ValueOf(&circle)
	fmt.Println(value2.CanSet())          // false
	fmt.Println(value2)                   // 0xc420086008


	value3 := reflect.ValueOf(&circle).Elem()
	fmt.Println(value3.CanSet())          // true
	fmt.Println(value3)                   // 6.28


	value3.SetFloat(3.14)
	fmt.Println(circle)                   // 3.14

2. reflect.Type


reflect.TypeOf(i interface{}) Type

因为reflect.Typeof的参数是空接口类型,因此可以接收任意类型的数据。 TypeOf()的返回值是这个接口类型对应的reflect.Type对象。

reflect.Type的定义方法


比如,对于一个结构体

type User struct {
	Name string
	Age int
	Sex string
}


func (user User) Say (msg string) error{
	fmt.Println("say",msg)
	return nil
}


func reflectionStruct(){
	user :=User{"wang",1,"m"}
	rType := reflect.TypeOf(user)
	fmt.Println(rType)
	fmt.Printf("%T,%T,%v,%v\n",user,rType,user,rType)
	fmt.Println("---------------------")


	// 数据类名
	fmt.Println(rType)           //  main.User
	// 数据类型
	fmt.Println(rType.Kind())    //  struct
	//数据的类名
	fmt.Println(rType.Name())    //  User
	//数据的完整全名(包含包)
	fmt.Println(rType.String())  // main.User
	//数据对象的字段个数
	fmt.Println(rType.NumField())  // 3
	//数据对象的方法个数
	fmt.Println(rType.NumMethod())  // 1


	fmt.Println("---------------------")


	rValue := reflect.ValueOf(user)
	fmt.Println(rValue)              // {wang 1 m}
	fmt.Println(rValue.Kind())       // struct
	fmt.Println(rValue.String())     // <main.User Value>
	fmt.Println(rValue.NumMethod())  // 1
	fmt.Println(rValue.NumField())   // 3

3. reflect.Value

reflect.ValueOf(i interface{}) Value
  • reflect.ValueOf()的返回值类型为reflect.Value,它实现了interface{}参数到reflect.Value的反射
  • reflact.Value对象可以通过调用Interface()方法,再反射回interface{}对象
  • 修改变量数据,通过变量指针类型的value.Elem(),可调用Set... 
  • 结构体属性的修改和方法的调用

a. 对于一般类型


见上面反射定律的例子

b. 对于结构体类型


可以通过Field和Method方法修改属性和调用方法

Field
  • 修改变量一定需要通过Elem()
  • 可使用FieldByName 或者Field(index)
type User struct {
	Name string
	Age int
	Sex string
}



func main(){
	user := User{"小小", 23,"Man"}
	rvalue := reflect.ValueOf(&user)
	rvalue.Elem().FieldByName("Sex").SetString("Woman")
	fmt.Println(user)                      // {小小 23 Woman}
	rvalue.Elem().Field(1).SetInt(10)
	fmt.Println(user)                      // {小小 10 Woman}


}



Method

  • 函数的调用用`MethodByName()`或者`Method(index)`的call方法
  • 返回值是一个数组
type User struct {
	Name string
	Age int
	Sex string
}


func (user User) Say (msg string) int {
	fmt.Println("say",msg)
	return 1234
}


func main(){


	args := []reflect.Value{reflect.ValueOf("hello 1")}


	method := rvalue.Method(0)
	method.Call(args)               // say hello 1
	


	method1 := rvalue.MethodByName("Say")
	t:= method1.Call(args)          // say hello 1
	fmt.Println(t)                  // [<int Value>]
	fmt.Println(t[0])               // 1234


}

猜你喜欢

转载自blog.csdn.net/zengchen73/article/details/80955831