本文已参与「新人创作礼」活动,一起开启掘金创作之路
引言
reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。
反射reflect.Value修改变量的值
reflect.Value是通过reflect.ValueOf(X)获得的,只有当X是指针的时候,才可以通过reflec.Value修改实际变量X的值,即:要修改反射类型的对象就一定要保证其值是“addressable”的。
也就是说:要想修改一个变量的值,那么必须通过该变量的指针地址 , 取消指针的引用 。通过refPtrVal := reflect.Valueof( &var )的方式获取指针类型,你使用refPtrVal.elem( ).set(一个新的reflect.Value)来进行更改,传递给set()的值也必须是一个reflect.value。
这里需要一个方法:
func (Value) Elem
func (v Value) Elem() Value
Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
如果你的变量是一个指针、map、slice、channel、Array。那么你可以使用reflect.Typeof(v).Elem()来确定包含的类型。
package main
import (
"fmt"
"reflect"
)
func main() {
var num float64 = 3.1415926
fmt.Println("num的数值:", num)
//需要操作指针
//通过reflect.ValueOf获取num中的reflect.Value,注意,参数必须是指针才能修改其值
pointer := reflect.ValueOf(&num)
newValue := pointer.Elem()
fmt.Println("类型 :", newValue.Type()) //float64
fmt.Println("是否可以修改:", newValue.CanSet())
// 重新赋值
newValue.SetFloat(666)
fmt.Println("新的数值:", num)
// 如果reflect.ValueOf的参数不是指针,会如何?
//尝试直接修改
//value := reflect.ValueOf(num)
//value.SetFloat(666) //panic: reflect: reflect.Value.SetFloat using unaddressable value
//fmt.Println(value.CanSet()) //false
//pointer = reflect.ValueOf(num)
//newValue = value.Elem() // 如果非指针,这里直接panic,“panic: reflect: call of reflect.Value.Elem on float64 Value”
}
运行结果
num的数值: 3.1415926
类型 : float64
是否可以修改: true
新的数值: 666
代码解释:
-
需要传入的参数是 *float64 这个指针,然后可以通过pointer.Elem()去获取所指向的Value,注意一定要是指针。
-
如果传入的参数不是指针,而是变量,那么
- 通过Elem获取原始值对应的对象则直接panic
- 通过CanSet方法查询是否可以设置返回false
-
newValue.CantSet()表示是否可以重新设置其值,如果输出的是true则可修改,否则不能修改,修改完之后再进行打印发现真的已经修改了。
-
reflect.Value.Elem() 表示获取原始值对应的反射对象,只有原始对象才能修改,当前反射对象是不能修改的
-
也就是说如果要修改反射类型对象,其值必须是“addressable”【对应的要传入的是指针,同时要通过Elem方法获取原始值对应的反射对象】
-
struct 或者 struct 的嵌套都是一样的判断处理方式
尝试修改结构体中的字段数值:
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
School string
}
func main() {
/*
通过反射,来更改对象的数值:前提是数据可以被更改
*/
s1:=Student{"王富贵",20,"MIT"}
fmt.Printf("%T\n",s1) //main.Student
p1:=&s1
fmt.Printf("%T\n",p1) //*main.Student
fmt.Println(s1.Name)
fmt.Println((*p1).Name,p1.Name)
v1:= reflect.ValueOf(&s1) // value
if v1.Kind()==reflect.Ptr{
fmt.Println(v1.Elem().CanSet())
v1 = v1.Elem()
}
f1:=v1.FieldByName("Name")
f1.SetString("诸葛青")
f3:=v1.FieldByName("School")
f3.SetString("蓝翔")
fmt.Println(s1)
}
运行结果:
main.Student
*main.Student
王富贵
王富贵 王富贵
true
{诸葛青 20 蓝翔}