Golang反射:无法设置包装结构的界面字段 [英] Golang reflection: Can't set fields of interface wrapping a struct

查看:90
本文介绍了Golang反射:无法设置包装结构的界面字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一种方法,该方法更改可以具有任意结构的对象中字段的值.当我有一个指向结构的指针时,字段的遍历是没有问题的.但是,当我有一个不包装指向结构的指针但包装结构本身的接口时,我无法更改字段,简而言之:

// The following doesn't work
var x interface{} = A{Str: "Hello"}
// This panics: reflect: call of reflect.Value.Field on ptr Value
reflect.ValueOf(&x).Field(0).SetString("Bye")
// This panics: reflect: call of reflect.Value.Field on interface Value
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye")
// This panics: reflect: reflect.Value.SetString using unaddressable value
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye")
// This prints `false`. But I want this to be settable
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet())

// This works
var z interface{} = &A{Str: "Hello"}
// This prints `true`
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet())

很长一段时间: http://play.golang.org/p/OsnCPvOx8F

我已阅读反射定律,所以我知道我只能修改当我有一个指向结构的字段时.所以我现在的问题是:如何获取结构数据的指针?

更新:

我基本上使用y := reflect.New(reflect.TypeOf(x))使它工作,所以现在可以设置y的值.有关广泛的示例,请参见: https://gist.github.com/hvoecking/10772475

解决方案

您似乎正在尝试修改存储在接口变量中的动态值.您只能对接口变量执行的操作是获取或设置动态值(进行复制的操作),并检查存储值的类型.

要了解为什么事情会这样,请想象有一个这样的操作,并且我们有以下代码:

var ptr *A = pointer_to_dynamic_value(x)
x = B{...}

ptr现在代表什么?在为接口变量分配新值时,该语言可以自由地重用存储,因此ptr现在可能指向B值的内存,这破坏了该语言的类型安全性(当前编译器的存储为仅保证可以重复使用以获取较小的值,但重点仍然存在.

更改存储在接口中的值的唯一安全方法是将值复制出来,然后分配回修改后的版本.例如:

a := x.(A)
a.Str = "Bye"
x = a

reflect软件包反映了这些限制,因此表示动态值字段的reflect.Value被视为只读.

您可以在第一个示例中设置字段,因为z的动态值是*A指针,而不是结构本身:这意味着可以修改引用的结构.

I am trying to implement a method that changes the value of fields in an object that can have an arbitrary structure. The traversion of the fields is no problem when I have the pointer to a struct. But I can not manage to change the fields when I have an interface that does not wrap a pointer to a struct but the struct itself, in short:

// The following doesn't work
var x interface{} = A{Str: "Hello"}
// This panics: reflect: call of reflect.Value.Field on ptr Value
reflect.ValueOf(&x).Field(0).SetString("Bye")
// This panics: reflect: call of reflect.Value.Field on interface Value
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye")
// This panics: reflect: reflect.Value.SetString using unaddressable value
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye")
// This prints `false`. But I want this to be settable
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet())

// This works
var z interface{} = &A{Str: "Hello"}
// This prints `true`
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet())

In long: http://play.golang.org/p/OsnCPvOx8F

I have read The Laws of Reflection so I am aware that I may only modify fields when I have a pointer to a struct. So my question is now: How do I get the pointer to the data of the struct?

UPDATE:

I got it working using basically y := reflect.New(reflect.TypeOf(x)) so the values of y are settable now. For an extensive example see this: https://gist.github.com/hvoecking/10772475

解决方案

You appear to be trying to modify the dynamic value stored inside an interface variable. The only operations you can perform on an interface variable are to get or set the dynamic value (operations that make copies), and to check the type of the stored value.

To understand why things are this way, imagine that there was such an operation and we had the following code:

var ptr *A = pointer_to_dynamic_value(x)
x = B{...}

What does ptr now represent? The language is free to reuse storage when assigning new values to an interface variable, so the the ptr might now point to the memory for the B value, which breaks the type safety of the language (with the current compilers storage is only guaranteed to be reused for small values, but the point remains).

The only safe way to mutate the value stored in an interface is to copy the value out, then assign back a the modified version. For example:

a := x.(A)
a.Str = "Bye"
x = a

The reflect package reflects these restrictions, so the reflect.Value representing the field of the dynamic value is considered read only.

You are able to set fields in your first example because the dynamic value for z is a *A pointer rather than the struct itself: this means the referenced struct can be modified.

这篇关于Golang反射:无法设置包装结构的界面字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆