英文:
Obtaining an addressable copy of a reflect.Value
问题
我想要为我接受的类型添加编组/解组的功能,类似于 JSON 的自定义编码/解码方式。当类型实现了 Marshal 方法(值接收器)和 Unmarshal 方法(指针接收器)时,我已经成功实现了这个功能。
显然,Unmarshal 方法必须使用指针接收器,以便保存新值。有人告诉我,使用 指针(而不是值)接收器的 Marshal 方法也是可以的。但问题在于,我正在使用的 reflect.Value 并不总是可寻址的。
以下是代码的编辑摘录,会导致 panic:
var v reflect.Value // 作为参数传入
t := v.Type()
pt := reflect.TypeOf(reflect.New(t).Interface())
if t.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) {
str, err = v.Interface().(Marshaler).MarshalXXX()
// ... 值接收器工作正常
} else if pt.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) {
str, err = v.Addr().Interface().(Marshaler).MarshalXXX()
// 指针接收器导致 PANIC:值不可寻址
我尝试创建 v 的副本,但副本也是不可寻址的,这对我来说没有意义:
tmp := reflect.New(v.Type())
tmp.Addr().Set(v)
str, err = tmp.Interface().(Marshaler).MarshalXXX()
如果我没有解释清楚,这里有一个在 Go Playground 上的示例,你可以尝试一下。
还有,当我在 pt
的赋值语句中获取指向类型的指针时,是否有更好的方法?
英文:
I want to add marshalling/unmarshalling to types that I accept, in a way similar JSON custom encoding/decoding. I have it all working great when the type implements a Marshal method (value receiver) and Unmarshal method (pointer receiver).
The Unmarshal method must obviously use a pointer receiver so that the new value is saved. I have been told that a Marshal method using a pointer (not value) receiver should also be allowed. The trouble with this is I am using a reflect.Value which is not always addressable.
Here's an edited extract of the code which panics:
var v reflect.Value // passed in parameter
t := v.Type()
pt := reflect.TypeOf(reflect.New(t).Interface())
if t.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) {
str, err = v.Interface().(Marshaler).MarshalXXX()
// ... value receiver WORKS FINE
} else if pt.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) {
str, err = v.Addr().Interface().(Marshaler).MarshalXXX()
// ptr receiver gives PANIC value is not addressable
I tried creating a copy of v, but the copy is also not addressable which makes no sense to me:
tmp := reflect.New(v.Type())
tmp.Addr().Set(v)
str, err = tmp.Interface().(Marshaler).MarshalXXX()
In case I have not explained this well here is an example on the Go Playground for you to try:
Also while I am here: Is there a better way to get the type of a pointer to a the type than in the assignment to pt
above?
答案1
得分: 4
我找到了答案。问题是我使用了Addr
而不是Elem
。尽管如此,这也引出了一个问题,为什么Set
方法能够正常工作,因为恐慌错误直到下一行才发生。
总结一下,要创建一个可寻址的 reflect.Value v
的副本,请按照以下步骤进行:
tmp := reflect.New(v.Type()) // 创建与 v 相同类型的零值
tmp.Elem().Set(v)
请原谅我过早地提问,但我已经花了4个小时来解决这个问题。为了完整起见,这里是我在问题中修复的 Go Playground 代码。
英文:
I found the answer. The problem was I was using Addr
instead of Elem
. Though that begs the question why Set
worked, since the panic did not occur till the following line.
In summary, to create an addressable copy of a reflect.Value v
do this:
tmp := reflect.New(v.Type()) // create zero value of same type as v
tmp.Elem().Set(v)
Please forgive me asking prematurely, but I had already spent 4 hours trying to solve the problem. For completeness here is the fix to my Go playground code in the question.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论