golang反射修改结构体字段(reflect.flag.mustBeAssignable using value obtained using unexported field) 2020.8.18

代码:

使用反射完成对Cal结构体字段的修改和方法的调用

package main

import (
	"fmt"
	"reflect"
)

type Cal struct {
	num1 float64
	num2 float64
}

func (cal Cal) GetSub(name string) {
	res := cal.num1 - cal.num2
	fmt.Printf("%s完成了减法运算,%f-%f=%f\n", name, cal.num1, cal.num2, res)
}

func ReflectCalTest(in interface{}) {

	inType := reflect.TypeOf(in)
	inVal := reflect.ValueOf(in)

	for i := 0; i < inType.Elem().NumField(); i++ {
		fmt.Printf("第%d个字段名为%v,类型为%v\n", i, inType.Elem().Field(i).Name, inType.Elem().Field(i).Type)
	}

	inVal.Elem().Field(0).SetFloat(8.0)

	inVal.Elem().Field(1).SetFloat(3.0)

	var inArg []reflect.Value

	inArg = append(inArg, reflect.ValueOf("Tom"))
	//append(inArg, reflect.ValueOf("Lisa"))

	inVal.Elem().Method(0).Call(inArg)

}

func main() {

	cal := Cal{
		num1: 18,
		num2: 2,
	}

	ReflectCalTest(&cal)
}

运行后报错

panic: reflect: reflect.flag.mustBeAssignable using value obtained using unexported field

goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x1ae)
        D:/Go/src/reflect/value.go:244 +0x1c0
reflect.flag.mustBeAssignable(...)
        D:/Go/src/reflect/value.go:234
reflect.Value.SetFloat(0x4dcee0, 0xc0000120b0, 0x1ae, 0x4020000000000000)
        D:/Go/src/reflect/value.go:1587 +0x3e
main.ReflectCalTest(0x4e65a0, 0xc0000120b0)
        D:/Go/src/zwmchz/reflectdemo/test01/main.go:27 +0x3cc
main.main()
        D:/Go/src/zwmchz/reflectdemo/test01/main.go:47 +0x67
exit status 2

查看27行后发现字段不可修改,官方文档对于CanSet()说明:只有一个Value持有值可以被寻址同时又不是来自非导出字段时,它才可以被修改。如果CanSet返回假,调用Set或任何限定类型的设置函数(如SetBool、SetInt64)都会panic。因此使用Set方法,对于值类型要求传递地址,即指针类型,且字段的访问范围应该是公有的,即首字母应该大写

由于错误是使用了非导出字段,将Cal结构字段名首字母更改为大写后,错误消失

修改后代码如下

package main

import (
	"fmt"
	"reflect"
)

type Cal struct {
	Num1 float64
	Num2 float64
}

func (cal Cal) GetSub(name string) {
	res := cal.Num1 - cal.Num2
	fmt.Printf("%s完成了减法运算,%f-%f=%f\n", name, cal.Num1, cal.Num2, res)
}

func ReflectCalTest(in interface{}) {

	inType := reflect.TypeOf(in)
	inVal := reflect.ValueOf(in)

	for i := 0; i < inType.Elem().NumField(); i++ {
		fmt.Printf("第%d个字段名为%v,类型为%v\n", i, inType.Elem().Field(i).Name, inType.Elem().Field(i).Type)
	}

	if inVal.Elem().Field(0).CanSet() {
		inVal.Elem().Field(0).SetFloat(8.0)
		fmt.Println("num1 is ok!")
	} else {
		fmt.Println("num1 is not ok!")
	}
	if inVal.Elem().Field(1).CanSet() {
		inVal.Elem().Field(1).SetFloat(3.0)
		fmt.Println("num2 is ok!")
	} else {
		fmt.Println("num2 is not ok!")
	}

	var inArg []reflect.Value

	inArg = append(inArg, reflect.ValueOf("Tom"))
	//append(inArg, reflect.ValueOf("Lisa"))

	inVal.Elem().Method(0).Call(inArg)

}

func main() {

	cal := Cal{
		Num1: 18,
		Num2: 2,
	}

	ReflectCalTest(&cal)
}

结果如下

第0个字段名为Num1,类型为float64
第1个字段名为Num2,类型为float64
num1 is ok!
num2 is ok!
Tom完成了减法运算,8.000000-3.000000=5.000000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值