正文

reflect.ValueOfreflect.TypeOf
Elem()reflect.ValueOfElem()reflect.ValueOf

那我们什么时候应该传递指针参数呢?

什么时候传递指针?

要回答这个问题,我们可以思考一下以下列出的几点内容:

chanmapsliceinterfaceTypeOfValueOfinterfaceinterfacev := reflect.ValueOf(&a)v.CanSet()falsev.Elem().CanSet()true
chanmapslice

1. 通过传递指针修改变量的值

panic

传值无法修改变量本身

vxxpanic

传指针可以修改变量

xreflect.ValueOfvxvx

2. 通过传递指针修改结构体的字段

reflect.ValueOfpanic

3. 结构体:获取指针接收值方法

对于结构体而言,如果我们想通过反射来调用指针接收者方法,那么我们需要传递指针。

在开始讲解这一点之前,需要就以下内容达成共识:

pM1&pM1M2pM2p&pM2

但是在反射的时候,我们是无法做到这一点的,这个需要特别注意。如果我们想通过反射来调用指针接收者的方法,就需要传递指针。

4. 变量本身包含指向数据的指针

最好不要通过值的反射对象来修改值的数据,就算有些类型可以实现这种功能。

chanmapslicereflect.ValueOf

通过值反射对象修改 chan、map 和 slice

chanmapsliceunsafe.Pointer
chanmapslice

slice 反射对象扩容的影响

mapslicemapslicemapslice

sliceslicearraysliceslicemapslicemap

示例代码:

v5v4arraysarrayv5s

虽然通过值反射对象可以修改 slice 的数据,但是如果通过反射对象 append 元素到 slice 的反射对象的时候, 可能会触发 slice 扩容,这个时候再修改反射对象的时候,就影响不了原来的 slice 了。

slice 容量够的话是不是就可以正常追加元素了?

只能说,能,也不能。我们看看下面这个例子:

slices1s1lens1
len
slice[0, len(s))

map 也不能通过值反射对象来修改其元素。

slicemap

但是,从另一个角度看,如果我们只是修改其元素的话,是可以正常修改的。

chan 没有追加

chanslicemapchan
chanch&chreflect.ValueOfch

结构体字段包含指针的情况

reflect.ValueOf
p&pNameNamepName
p

5. interface 类型处理

interface

我们可以通过下面的断言来证明:

当然,对于指针类型也是一样的:

同样的,我们可以通过下面的断言来证明:

interface 底层类型是值

interfacereflect.ValueOfinterface
v := reflect.ValueOf(i)v := reflect.ValueOf(p)reflect.ValueOf(p)pinterface{}ipinterface{}reflect.ValueOf

interface 底层类型是指针

interfacereflect.ValueOfinterfacereflect.ValueOf

不要再对接口类型取地址

能不能通过反射 Value 对象来修改变量只取决于,能不能根据反射对象拿到最初变量的内存地址。 如果拿到的只是原始值的拷贝,不管我们怎么做都无法修改原始值。

对于初学者另外一个令人困惑的地方可能是下面这样的代码:

reflect.ValueOf()interface{}
reflectreflect.ValueOf()
v2interface{}a
aai
iaaiv2

上图说明:

iaint&iefacev2efacev2iia
aa

6. 指针类型反射对象不可修改其指向地址

其实这一点上面有些地方也有涉及到,但是这里再强调一下。一个例子如下:

说明:

v&aElem()aElem()
Elem()

7. 反射也不能修改字符串中的字符

String

在 go 中,字符串是用一个结构体来表示的,大概长下面这个样子:

DataLen
str[1] = 'a'

相同的字符串只有一个实例

假设我们定义了两个相同的字符串,如下:

s1s2hello

两个字符串内存表示如下:

s1s2

字符串本身可以替换

虽然我们不能修改字符串中的某一个字符,但是我们可以通过反射对象把整个字符串替换掉:

sworldhelloworld

这可以用下图表示:

总结

reflect.ValueOf()chanmapslicereflect.ValueOf()mapslicemapsliceinterfaceinterfaceinterfaceinterfacestirng

但其实说了那么多,简单来说只有一点,就是我们只能通过反射对象来修改指针类型的变量。如果拿不到实际存储数据的指针,那么我们就无法通过反射对象来修改其中的元素了。