代码:

使用反射完成对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

到此这篇关于“golang反射修改结构体字段(reflect.flag.mustBeAssignable using value obtained using unexported field) ...”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 结构体断言_Golang中的reflect原理
Go语言--反射(reflect)
图解 Go 反射实现原理
golang 反射_golang面试题:reflect(反射包)如何获取字段tag?为什么json包不能导出私有变量的tag?...
Golang使用reflect操作struct
活学活用golang的反射机制
golang 结构体标签_如何在Go中使用结构标签
Golang结构体中Tag的使用
Java基础篇:反射机制详解
golang json[]