golang的反射实现原理 Go反射简介 如何通过反射对象来修改原数据对象的值呢?

反射机制是现代编程语言中一个比较高级的特性

在编译时不知道类型的情况下,通过反射机制可以获取对象的类型、值、方法甚至动态改变对象的成员,这就是反射机制 

golang的反射实现原理
Go反射简介
如何通过反射对象来修改原数据对象的值呢?

 

反射的意思是在运行时,能够动态知道给定数据对象的类型和结构,并有机会修改它!

现在一个数据对象,如何判断它是什么结构?数据interface中保存有结构数据呀,只要想办法拿到该数据对应的内存地址,然后把该数据转成interface,通过查看interface中的类型结构,就可以知道该数据的结构了呀~ 其实以上就是Go反射通俗的原理。

Go反射有三大法则:

1. Reflection goes from interface value to reflection object.

  接口数据 -> 反射对象

golang的反射实现原理
Go反射简介
如何通过反射对象来修改原数据对象的值呢?

2. Reflection goes from reflection object to interface value. 

  反射对象 ===> 接口数据

golang的反射实现原理
Go反射简介
如何通过反射对象来修改原数据对象的值呢?

...
v := reflect.ValueOf(x)       
o := v.Interface().(float64)
...

3. To modify a reflection object, the value must be settable.

  倘若数据可更改,可通过反射对象来修改它

Go 的反射就是对以上三项法则的实现。

Go的反射主要由两部分组成:Type和 Value, Type和 Value是俩结构体。

你会发现反射的实现和interface的组成很相似,都是由“类型”和“数据值”构成,但是值得注意的是:interface的“类型”和“数据值”是在“一起的”,而反射的“类型”和“数据值”是分开的。

Type和 Value提供了非常多的方法:例如获取对象的属性列表、获取和修改某个属性的值、对象所属结构体的名字、对象的底层类型(underlying type)等等

Go中的反射,在使用中最核心的就两个函数:

  • reflect.TypeOf(x)
  • reflect.ValueOf(x)

这两个函数可以分别将给定的数据对象转化为以上的 Type和 Value。这两个都叫做 反射对象

如何通过反射对象来修改原数据对象的值呢?

在目前以上的所有例子中,反射得到的 Value对象的修改,都是无法直接修改原数据对象的。

如何才能可以通过反射对象来修改原数据对象的值或者说为什么不能设置呢?

原因简单且纯粹:在Go中,任何函数的参数都是值的拷贝,而非原数据。

反射函数 reflect.ValueOf()也不例外。我们目前得到的反射对象,都是原对象的copy的反射对象,而非原对象本身,所以不可以修改到原对象;即使可以修改,修改一个传参时候的副本,也毫无意义,不如报错儿。Go反射第三法则中的制定的 settable属性就由此而来,还延伸出了类似于 CanSet()的方法。

那如何修改呢?

首先,在Go中要想让函数“有副作用“,传值必须传指针类型的。

...    
var x float64 = 3.4 
v := reflect.ValueOf(&x)
...

此时还不行,因为这样反射对象对应的是原数据对象的指针类型,必须要拿到当前类型的值类型(*v),如何做?Go提供了另外一个方法 Elem()

 ...    
var x float64 = 3.4    
v := reflect.ValueOf(&x)    
p := v.Elem()    
fmt.Println(p.CanSet()) // true    
p.SetFloat(7.1)    
fmt.Println(x) // 7.1

看以上代码,就可以修改原数据了。