Dig101: dig more, simplified more and know more
我们都知道Go
的struct
里,小写字段是非导出的,即不可从包外部访问。
但非导出字段在外部也并不是没有办法访问,也不是不可以修改。
今天看下reflect
包如何在包外操作非导出字段。
取地址访问
先来看第一个函数NewAt
:
对于结构体,通过其底层地址(指针p)和类型,返回指向该结构体的一个指针,
该值是可寻址的(addressable
),即可访问该结构体
1 | // reflect/value.go |
有个这个方法,就可以通过struct
的反射获取非导出字段
比如访问,对于如下含有非导出字段的结构体Example
1 | package testData |
便可以通过对结构体eg
取地址的方式,获取其非导出字段a
的内容
这里Elem
是获取其底层数据对象的方式,
如果知道类型,也可显示指定调用,如reflect.value.Interface,reflect.value.Int...
1 | var eg testData.Example |
这里注意必须要对eg
取地址, 否则会panic
:
panic: reflect: call of reflect.Value.Elem on struct Value
因为reflect.Value.Elem
需要reflect.Value
类型必须是interface
或者ptr
,
这样获取其底层的值才有意义:要么返回interface
底层的值或者ptr
指向的值
其注释如下:
1 | // Elem returns the value that the interface v contains |
取地址修改
那可以访问了,如何修改呢?
利用reflect.value.Set
就可以:
上边Elem
获取到的反射值是可修改的(assignable
),突破了非导出字段不能从外部修改的限制
1 | var eg testData.Example |
这里是以反射值来修改非导出字段值,内部类型须一致。修改后内容会直接反应到eg
上
类似的还有指定类型的设置方法如SetString,SetBool...
非取地址访问
当然不取地址也是可以访问非导出字段的。
这里用到的第二个函数是New
:
基于指定类型创建一个可以表示该类型的指针
1 | // New returns a Value representing a pointer to a new zero value |
具体访问代码如下:
1 | func GetStructUnExportedField(source interface{}, fieldName string) (accessableField, addressableSourceCopy reflect.Value) { |
这样其实是内部构造了一个对该结构其取地址的指针,以满足后续调用Elem
时可寻址!
非取地址修改
非取地址的方式访问没有问题,要还想修改就不会反应到原始结构体上了
毕竟是内部重新拷贝了一个结构体进行的操作。
具体操作类似取地址修改的方式,这里不赘述了。
实际使用中,还是通过NewAt
获取可读写的非导出字段更方便一些。
本文代码见 NewbMiao/Dig101-Go
如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com 本文链接 : https://newbmiao.github.io/2020/06/13/dig101-golang-reflect-handle-unexport-field.html