golang反射赋值类型异常 “error“: “reflect.Set: value of type *xxx is not assignable to type xxx 底层原因与解决方法

在上一篇文章中我们说到 golang在通过反射赋值时,要赋值的数据类型必须是一个指针, 且我们在对一个指针类型的数据在获取 reflect.Value对象时,要对其数据进行操作还必须要调用.Elem()方法获取指针对应的值后才能进行。详细参见golang中通过反射给对象赋值 reflect.Value,reflect.Type, 自定义异常 最佳实践-CSDN博客文章浏览阅读316次,点赞6次,收藏9次。go里面的自定义异常非常简单, 你直接自定义一个error的类型即可,见上面的示例。在go里面,error是一个内置的接口定义,他就定义了一个方法Error() string, 所以,在go语言里面,其实只要你的对象只要是实现了方法 Error() string 则你的这个对象就可以作为一个自定义的异常来使用。error接口定义参考reflect.Value实现了reflect.Type中定义的所有方法,是Type接口的具体实现。https://blog.csdn.net/tekin_cn/article/details/140455338

今天的这个异常 reflect.Set: value of type *xxx is not assignable to type xxx  他的底层原因和我们的上一篇文章密切相关,核心就是对.Elem()方法的理解和应用。

异常代码示例

要使用反射赋值的对象 res 已经是一个指针了,没有加 & 在使用反射赋值(v.Elem().Set(reflect.ValueOf(val)))的时候 就会抛出异常,原因就是要设置的值的类型和当前的对象类型不匹配

func (s *SoftwareSvc) List(c *ginx.XContext) (*dto.DlSoftwareListRes, error) {
	ckey := "softlist:xxx"
	// 初始化返回对象
	res := &dto.DlSoftwareListRes{}
	// 从缓存中读取数据 注意这里的 res已经是一个指针了,下面res是要使用反射赋值的对象
	if err := global.CacheGet(ckey, res); err != nil {
		// 重新从数据库获取数据 ......
		
		// 缓存数据 这里的res 就是一个指针类型 *dto.DlSoftwareListRes 
		if err := global.CacheSet(ckey, res, 0); err != nil {
			global.Log.Errorf("写缓存失败! ckey:%s err: %v ", ckey, err.Error())
		}
	}
	return res, nil
}

异常底层原因图解分析

res := &dto.DlSoftwareListRes{} 这里的res虽然是一个指针,满足了反射赋值的类型要求,但是我们要赋值的对象也是一个指针这个res在反射Value对象后调用.Elem()方法后他的类型就不再是指针,而是指针指向的值,然后我们再将一个指针类型的数据赋给一个非指针类型,故抛Panic异常。
 

下面我们以gin-cache的 InMemoryStore里面的反射赋值为例图解说明, 

上图显示了我们要赋值的对象value和我们将要赋值的值都是一个指针,且类型完全相同。

 这里对要赋值的对象调用了.Elem()方法后,在调用Set进行赋值,此时的对象和要赋值的值类型就不一致了。 一个是值,一个是值的指针, 所以就panic异常了

解决方法:

对于要赋值的值是一个指针的, 我们在传递赋值变量的时候需要使用指针的指针, 即对要赋值的指针变量在加一个 & ,让这个变量变成指针的指针,这样在反射Value里面调用.Elem()方法后获取到的对象还是一个指针, 这时在将指针数据Set给指针就没有问题了。

对应上面的异常代码,只需要将 global.CacheGet(ckey, res) 修改为 global.CacheGet(ckey, &res) 即可。

图示

总结 

 golang中的反射赋值,搞明白.Elem()方法是关键, 出这个问题的原因就是类型不匹配,而根源是.Elem()方法获取了指针指向的值,这时当前的对象的值的类型就发生了改变,所以我们在反射里面调用xx.Elem().Set(reflect.ValueOf(xxx)) 对对象进行赋值时就需要对对象的值做相应的调整。

一句话就是:不管你要赋值的值是什么类型,我们在使用反射传递要赋值的对象的时候都必须要加上&

  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值