golang 如何用反射reflect操作结构体
作者:皿小草 发布时间:2024-05-08 10:22:45
标签:golang,反射,reflect,结构体
背景
需要遍历结构体的所有field
对于exported的field, 动态set这个field的value
对于unexported的field, 通过强行取址的方法来获取该值(tricky?)
思路
下面的代码实现了从一个strct ptr对一个包外结构体进行取值的操作,这种场合在笔者需要用到反射的场合中出现比较多
simpleStrtuctField 函数接受一个结构体指针,因为最后希望改变其值,所以传参必须是指针。然后解引用。
接下来遍历结构体的每个field, exported字段是CanInterface的,对于unexported字段,需要强行取址来获取其值
model.go
package model
type Person struct {
Name string
age int
}
func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
age: age,
}
}
main.go
package main
import (
"github.com/miaomiao3/log"
"../model"
"reflect"
"unsafe"
)
func main() {
person := model.NewPerson("haha", 12)
log.Debug("before:%+v", person)
simpleStrtuctField(person)
simpleStrtuctField(person)
log.Debug("after:%+v", person)
}
// get unexported field
func simpleStrtuctField(v interface{}) {
dataType := reflect.TypeOf(v)
dataValue := reflect.ValueOf(v)
if dataType.Kind() == reflect.Ptr {
if dataValue.IsNil() {
panic("nil ptr")
}
// 如果是指针,则要判断一下是否为struct
originType := reflect.ValueOf(v).Elem().Type()
if originType.Kind() != reflect.Struct {
return
}
// 解引用
dataValue = dataValue.Elem()
dataType = dataType.Elem()
} else {
panic("non ptr")
}
num := dataType.NumField()
for i := 0; i < num; i++ {
field := dataType.Field(i)
fieldName := field.Name
fieldValue := dataValue.FieldByName(fieldName)
if !fieldValue.IsValid() {
continue
}
if fieldValue.CanInterface() {
log.Debug("exported fieldName:%v value:%v", fieldName, fieldValue.Interface())
if fieldValue.CanSet() && fieldValue.Kind() == reflect.String {
oldValue := fieldValue.Interface().(string)
fieldValue.SetString(oldValue + " auto append")
}
} else {
// 强行取址
forceValue := reflect.NewAt(fieldValue.Type(), unsafe.Pointer(fieldValue.UnsafeAddr())).Elem()
log.Debug("unexported fieldName:%v value:%v", fieldName, forceValue.Interface())
}
}
}
output:
2019/06/02 17:15:31.64 [D] before:&{Name:haha age:12}
2019/06/02 17:15:31.64 [D] exported fieldName:Name value:haha
2019/06/02 17:15:31.64 [D] unexported fieldName:age value:12
2019/06/02 17:15:31.64 [D] after:&{Name:haha auto append age:12}
可以看到,Name字段被反射改变了,age的值也已经获取到
补充:go语言通过反射创建结构体、赋值、并调用对应方法
看代码吧~
package main
import (
"fmt"
"reflect"
"testing"
)
type Call struct {
Num1 int
Num2 int
}
func (call Call) GetSub(name string){
fmt.Printf("%v 完成了减法运算,%v - %v = %v \n", name, call.Num1, call.Num2, call.Num1 - call.Num2)
}
func (call *Call) GetSum(name string){
fmt.Printf("%v 完成了加法运算,%v + %v = %v \n", name, call.Num1, call.Num2, call.Num1 + call.Num2)
}
func TestReflect(t *testing.T) {
var (
call *Call
rValues []reflect.Value
rValues2 []reflect.Value
)
ptrType := reflect.TypeOf(call) //获取call的指针的reflect.Type
trueType := ptrType.Elem() //获取type的真实类型
ptrValue := reflect.New(trueType) //返回对象的指针对应的reflect.Value
call = ptrValue.Interface().(*Call)
trueValue := ptrValue.Elem() //获取真实的结构体类型
trueValue.FieldByName("Num1").SetInt(123)//设置对象属性,注意这个一定要是真实的结构类型的reflect.Value才能调用,指针类型reflect.Value的会报错
//ptrValue.FieldByName("Num2").SetInt(23)
trueValue.FieldByName("Num2").SetInt(23)
//rValues = make([]reflect.Value, 0)
rValues = append(rValues, reflect.ValueOf("xiaopeng"))//调用对应的方法
fmt.Println(rValues)
trueValue.MethodByName("GetSub").Call(rValues)
/*
fixme 在反射中,指针的方法不可以给实际类型调用,实际类型的方法可以给指针类型调用,因为go语言对这种操作做了封装
所以下面一句是没问题的
下下一句会运行时报错
*/
//ptrValue.MethodByName("GetSub").Call(rValues)
//trueValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
ptrValue.MethodByName("GetSum").Call(append(rValues2, reflect.ValueOf("hiram")))
fmt.Println(call)
/*
fixme 在实际使用中 指针和实体都能相互转换,不会影响调用
但是指针的方法在方法体内的操作会影响到结构体本身属性
而实体的方法不会,因为go对于结构体、数组、基本类型都是值传递
*/
call.GetSub("aaa")
(*call).GetSub("bbb")
call.GetSum("ccc")
(*call).GetSum("ddd")
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持asp之家。如有错误或未考虑完全的地方,望不吝赐教。
来源:https://blog.csdn.net/oqqYuan1234567890/article/details/90741112


猜你喜欢
- 重装电脑,在windows和虚拟机里面的Ubuntu里都安装了Pycharm专业版,安装的时候我都选择了vim插件,装好之后打开发现ctrl
- 常见到网上博文有错误理论,包括身边很多朋友都曾认同“可访问性(Accessibility)是为残障人士准备”的观点。其实在互联网技术领域远不
- 简介python可以做很多事情,虽然它的强项在于进行向量运算和机器学习、深度学习等方面。但是在某些时候,我们仍然需要使用python对外提供
- 在执行任何查询时,SQL Server都会将数据读取到内存,数据使用之后,不会立即释放,而是会缓存在内存Buffer中,当再次执行相同的查询
- 一. 语法1.1 获取当前页面的所有cookie:var allCookies = document.cookie;allCookies 是
- 问题记录一下出现的问题, 数据翻倍这是复现问题的代码data() { return { space: "
- 前言:NumPy 是 Python 语言的一个扩充程序库,支持大量高维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。同时NumP
- Bytes和Str的区别在Python3中,字符序列有两种类型:bytes和str。bytes类型是无符号的8位值(通常以ASCII码显式)
- 第一步把tensorflow保存的.ckpt模型转为pb模型, 并记下模型的输入输出名字.第二步去ncnn的github上把仓库clone下
- 前言我们在写应用时,基本都会用到配置文件,从各种 shell 到 nginx 等,都有自己的配置文件。虽然这没有太多难度,但是配置项一般相对
- 问题在Django中使用mysql偶尔会出现数据库连接丢失的情况,错误通常有如下两种OperationalError: (2006,
- 如何用Response.Write调用代替内嵌表达式?我们可以利用下面的代码,注意:代码的每一行对响应流有一次写操作,所有的代码都包含在一个
- 简单介绍下功能吧:使用了ASP的一个对象ServerVariables(服务器环境变量),通过这个环境变量可以获取到真正的下载地址再通过一些
- 使用php,定义php的默认语言. php.ini中: default_charset = "gb2312" 在网页中输
- 1、TCP连接的建立方法客户端在建立一个TCP连接时一般需要两步,而服务器的这个过程需要四步,具体见下面的比较。步骤TCP客户端TCP服务器
- PHP addAttribute() 函数实例给根元素和 body 元素添加一个属性:<?php $note=<<<
- 一、介绍make_blobs() 是 sklearn.datasets中的一个函数。主要是产生聚类数据集,产生一个数据集和相应的标签。函数的
- .xls格式 Office2003及以下版本 .xlsx格式Offi
- 在设计主键的时候往往需要考虑以下几点: 1.无意义性:此处无意义是从用户的角度来定义的。这种无意义在一定程度上也会减少数据库的信息冗余。常常
- numpy.mean计算矩阵均值计算矩阵的均值>>> a = np.array([[1, 2], [3, 4]])>