golang 如何用反射reflect操作结构体

作者:皿小草 时间:2024-05-08 10:22:45 

背景

需要遍历结构体的所有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

标签:golang,反射,reflect,结构体
0
投稿

猜你喜欢

  • HTML的基本元素

    2010-03-16 12:39:00
  • layer实现登录弹框,登录成功后关闭弹框并调用父窗口的例子

    2024-04-22 12:46:06
  • MySQL在命名用过程中所遇到的常见问题

    2008-12-05 16:03:00
  • 如何使用 Vuex的入门教程

    2024-05-28 16:00:48
  • python批量修改交换机密码的示例

    2023-06-29 07:52:42
  • sqlserver 2000中创建用户的图文方法

    2024-01-18 20:02:39
  • Python笔记之facade模式

    2023-10-18 22:59:50
  • uniapp路由uni-simple-router实例详解

    2023-09-24 21:27:04
  • 交互设计实用指南系列(10)—别让我思考

    2010-03-01 12:50:00
  • asp实现树型结构

    2008-04-13 06:06:00
  • Pytorch模型迁移和迁移学习,导入部分模型参数的操作

    2021-08-05 09:18:16
  • python使用递归解决全排列数字示例

    2022-02-22 04:14:49
  • CSS高级文字排版的实例

    2009-03-24 20:56:00
  • 深入理解Pytorch中的torch. matmul()

    2023-06-03 05:29:18
  • python中关于range()函数反向遍历的几种表达

    2023-07-09 20:23:33
  • 基于python的itchat库实现微信聊天机器人(推荐)

    2021-11-30 13:54:21
  • Python基于pillow判断图片完整性的方法

    2021-05-12 21:55:50
  • AJAX打造博客无刷新搜索

    2007-08-23 08:48:00
  • Python爬虫分析汇总

    2022-08-28 06:19:38
  • Python2包含中文报错的解决方法

    2021-09-12 20:51:24
  • asp之家 网络编程 m.aspxhome.com