Dig101: dig more, simplified more and know more

我们都知道Gostruct里,小写字段是非导出的,即不可从包外部访问。

但非导出字段在外部也并不是没有办法访问,也不是不可以修改。

今天看下reflect包如何在包外操作非导出字段。

文章目录

  • 取地址访问

  • 取地址修改

  • 非取地址访问

  • 非取地址修改

取地址访问

先来看第一个函数NewAt

对于结构体,通过其底层地址(指针 p)和类型,返回指向该结构体的一个指针

该值是可寻址的(addressable),即可访问该结构体

// reflect/value.go// NewAt returns a Value representing a pointer to a value of the// specified type, using p as that pointer.func NewAt(typ Type, p unsafe.Pointer) Value {  fl := flag(Ptr)  t := typ.(*rtype)  return Value{t.ptrTo(), p, fl}}

有个这个方法,就可以通过struct的反射获取非导出字段

比如访问,对于如下含有非导出字段的结构体Example

package testDatatype Example struct {  a string}

便可以通过对结构体eg取地址的方式,获取其非导出字段a的内容

这里Elem是获取其底层数据对象的方式,

如果知道类型,也可显示指定调用,如reflect.value.Interface, reflect.value.Int...

var eg testData.Examplea:=GetStructPtrUnExportedField(&eg, "a").String()

func GetStructPtrUnExportedField(source interface{}, fieldName string) reflect.Value {  // 获取非导出字段反射对象  v := reflect.ValueOf(source).Elem().FieldByName(fieldName)  // 构建指向该字段的可寻址(addressable)反射对象  return reflect.NewAt(v.Type(), unsafe.Pointer(v.UnsafeAddr())).Elem()}

这里注意必须要对eg取地址, 否则会panic:

panic: reflect: call of reflect.Value.Elem on struct Value

因为reflect.Value.Elem需要reflect.Value类型必须是interface或者ptr,

这样获取其底层的值才有意义:要么返回interface底层的值或者ptr指向的值

其注释如下:

// Elem returns the value that the interface v contains// or that the pointer v points to.// It panics if v's Kind is not Interface or Ptr.// It returns the zero Value if v is nil.func (v Value) Elem() Value {

取地址修改

那可以访问了,如何修改呢?

利用reflect.value.Set就可以:

上边Elem获取到的反射值是可修改的(assignable),突破了非导出字段不能从外部修改的限制

var eg testData.Exampleerr := SetStructPtrUnExportedStrField(&eg, "a", "test")

func SetStructPtrUnExportedStrField(source interface{}, fieldName string, fieldVal interface{}) (err error) {  v := GetStructPtrUnExportedField(source, fieldName)  rv := reflect.ValueOf(fieldVal)  if v.Kind() != rv.Kind() {    return fmt.Errorf("invalid kind: expected kind %v, got kind: %v", v.Kind(), rv.Kind())  }  // 修改非导出字段值  v.Set(rv)  return nil}

这里是以反射值来修改非导出字段值,内部类型须一致。修改后内容会直接反应到eg

类似的还有指定类型的设置方法如SetString,SetBool...

非取地址访问

当然不取地址也是可以访问非导出字段的。

这里用到的第二个函数是New:

基于指定类型创建一个可以表示该类型的指针

// New returns a Value representing a pointer to a new zero value// for the specified type. That is, the returned Value's Type is PtrTo(typ).func New(typ Type) Value {  if typ == nil {    panic("reflect: New(nil)")  }  t := typ.(*rtype)  ptr := unsafe_New(t)  fl := flag(Ptr)  return Value{t.ptrTo(), ptr, fl}}

具体访问代码如下:

func GetStructUnExportedField(source interface{}, fieldName string) (accessableField, addressableSourceCopy reflect.Value) {  v := reflect.ValueOf(source)  // since source is not a ptr, get an addressable copy of source to modify it later    addressableSourceCopy = reflect.New(v.Type()).Elem()    // make a copy of source  addressableSourceCopy.Set(v)  accessableField = addressableSourceCopy.FieldByName(fieldName)  accessableField = reflect.NewAt(accessableField.Type(), unsafe.Pointer(accessableField.UnsafeAddr())).Elem()  return}

这样其实是内部构造了一个对该结构取地址的指针,以满足后续调用Elem时可寻址!

非取地址修改

非取地址的方式访问没有问题,要还想修改就不会反应到原始结构体上了

毕竟是内部重新拷贝了一个结构体进行的操作。

具体操作类似取地址修改的方式,这里不赘述了。

实际使用中,还是通过NewAt获取可读写的非导出字段更方便一些。

本文代码见 NewbMiao/Dig101-Go


推荐阅读

  • Dig101-Go之如何在函数内修改指针

如果有用,点个 在看,让更多人看到

外链不能跳转,戳

go reflect 取指针_Go之如何操作结构体的非导出字段相关推荐

  1. go reflect 取指针_Go的方法集详解

    女主宣言 Go语言以其本身具有的高并发特性,在云计算开发中,得到了广泛的应用,也深受广大开发者的欢迎.但是大家对go语言真的了解了么?本文作者经过对go语言的多年实践应用,现对go语言中的方法集进行了 ...

  2. 利用返回引用来操作结构体

    1 #include "stdafx.h" 2 #include <iostream.h> 3 #include <string.h> 4 5 /***** ...

  3. C语言:指针的偏移步长、结构体成员的偏移量、嵌套结构体成员的偏移量、结构体的内存对齐

    文章目录 1 不同类型指针的偏移步长 2 结构体成员的偏移量 3 嵌套结构体成员的偏移量 4 结构体的内存对齐 4.1 内存对齐的原因与优点 4.2 结构体内存对齐的规则 4.3 结构体嵌套结构体时的 ...

  4. 用结构体指针指向(-)或结构体变量加点(.)后不出现结构体成员

    今天写代码时遇到这么个问题:用结构体指针指向(->)或结构体变量加点(.)后不出现结构体成员,虽然不影响编写,但效率降低,容易出错. 代码入下: stack.h #ifndef __STACK_ ...

  5. 【C 语言】结构体 ( 指针运算与指针内存操作 | 结构体成员偏移量计算 )

    文章目录 一.指针运算 与 指针内存操作 二.结构体偏移量计算 一.指针运算 与 指针内存操作 指针变量算术运算 ( 指针可以是任意值 ) : 指针 是一个变量 , 如果对指针进行 算术 / 逻辑 等 ...

  6. C语言—用结构体指针给数组赋值(结构体指针指向字符串,给字符串赋值)

    数组定义: char acBuf[sizeof(TRANS_HEAD_S) + sizeof(USER_HEAD_S) + 4] = {0}; 结构体定义: typedef struct {int i ...

  7. c语言中指针数组赋值字符串,C语言—用结构体指针给数组赋值(结构体指针指向字符串,给字符串赋值)...

    数组定义: char acBuf[sizeof(TRANS_HEAD_S) + sizeof(USER_HEAD_S) + 4] = {0}; 结构体定义: typedef struct { int ...

  8. go结构体初始化_go语言基础之结构体普通变量初始化

    1.结构体 1.1.结构体类型 有时我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号/姓名/性别/年龄/地址等属性.显然单独定义以上变量比较繁琐,数据不便于管理. 结构体是一种聚合的数 ...

  9. c语言中定义结构体指针的作用,C语言结构体定义,使用及指针(1)

    前言 C语言中的结构体作为一大难点,并且对于需要学习数据结构的同学来说,这个结构是每次上课都会遇到的,所以,我们只有更深层次的理解结构体的知识,才能更好的掌握数据结构 结构体用处 结构体到底有什么用? ...

最新文章

  1. oracle 分区使用情况,Oracle Hash分区的使用总结
  2. AAAI | 深度生成模型—NEVAE
  3. pointnet 结果可视化_PointNet论文复现及代码详解
  4. Linux Kernel TCP/IP Stack — 协议栈发包处理流程
  5. 第四十六期:关于云存储的五大优势
  6. 数字农业WMS库存操作重构及思考
  7. 新年UI的拆红包源码/5级代理功能/会员中心充值接口完善
  8. CleanCodeHandbook Chapter 6: Misc(35-38)
  9. 中国晶体谐振器行业市场供需与战略研究报告
  10. 还不知道事务消息吗?这篇文章带你全面扫盲!
  11. PEST分析顺丰服务需求_这个工作值不值得去做?4种分析模型及3种曲线让你学会观察行业...
  12. FPGA原型验证、硬件加速器(emulater 模拟器)、和aws FPGA公有云区别
  13. Ubuntu20.04 Clion/Pycharm/IDEA 输入中文+光标跟随解决方案
  14. SCU 4487 king's trouble I
  15. C#保存图片、压缩图片大小、缩放图片比例
  16. 妮可妮可妮 [Hash]
  17. 【软件工程】绘制状态转换图
  18. 穷养儿,富养女一一原来是指这样
  19. HCIP课程笔记-06-DR/BDR、OSPF各网络环境下的工作方式、OSPF的hello包、DBD包、LSR包、LSU包、LSAck;
  20. 北邮智能车仿真培训(九)—— 室外光电创意组仿真

热门文章

  1. react领域里面jsx中的style的值为什么放到了{}中
  2. git远程仓库的使用
  3. 给入门程序员20条编程经验
  4. boost库编译安装以及Qt导入
  5. Python爬虫实战八之利用Selenium抓取淘宝匿名旺旺
  6. Python 中 3 个不可思议的返回
  7. JDK可视化工具 | JConsole操作指南,手把手教会你
  8. pip安装OpenCV
  9. Ajax/JavaScript脚本大全,JS脚本大全
  10. 138个被撤出EI检索的会议