Golang反射.Value的行为

huangapple go评论93阅读模式
英文:

Golang reflection.Value behaviour

问题

我目前对golang的反射包的行为感到绝望,因为在我看来它似乎一点也不一致。

1)据我所了解,reflect.Value似乎携带了指向底层值的指针。
例如,如果我调用

var s string
v1 := reflect.ValueOf(&s).Elem()
v2 := v1
v2.SetString("Hello World!")
fmt.Println(s)

它会打印出"Hello World!"。
然而,对于通过调用Field()获得的reflect.Value来说,这似乎并不成立。

val := ... //将一个reflect.Value赋值给它
nextval := val.Field(0) //确保Field存在且为map类型
nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
fmt.Println(nextval.MapKeys()
fmt.Println(val.Field(index).MapKeys())

这将打印出

[Some_value_of_type_KEY]
[]

这是一个很大的烦恼。有人知道为什么会这样吗?

===================================================

2)考虑以下函数

func Test(v interface{}) {
    val := reflect.ValueOf(v)
    if val.Kind() != reflect.Struct {
        fmt.Println("It is a struct")
    }
}

如果我用任何结构体作为参数调用它,它会打印出"This is a struct"。
然而,我将无法通过使用val来给v内部的东西赋新值,因为该值不可寻址。通过以下方式解决:

func Test(v interface{}) {
    val := reflect.ValueOf(&v).Elem()
    if val.Kind() != reflect.Struct {
        fmt.Println("This never gets printed!")
    }
}

根据文档,我会认为通过取"&"我使用了v的指针,并且通过调用Elem()我得到了它指向的元素,因此val.Kind()应该仍然返回相同的结果。但事实并非如此。val.Kind()现在是reflect.Interface。

有没有办法不去

valForTestingKind := reflect.ValueOf(v)
valForSettingNewValue := reflect.ValueOf(&v).Elem()

因为这种方式感觉不对。

英文:

I'm currently getting desperate over the behaviour of golangs reflect package, which to me doesn't seem consistent at all.

  1. As far as I understand it, a reflect.Value seems to carry a pointer to the underlying value.
    E.g. if I call

    var s string
    v1 := reflect.ValueOf(&s).Elem()
    v2 := v1
    v2.SetString("Hello World!")
    fmt.Println(s)
    It prints me "Hello World!".
    However, this doesn't seem to hold true for a reflect.Value obtained by a call to Field().

    val := ... //Assign a reflect.Value to it
    nextval := val.Field(0) //Make sure that Field exists and is of type map
    nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
    nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
    fmt.Println(nextval.MapKeys()
    fmt.Println(val.Field(index).MapKeys())

This prints

[Some_value_of_type_KEY]
[]

which is a major annoyance. Anyone knows why this is the case?

===================================================

  1. Consider the function

    func Test(v interface{}) {
    val := reflect.ValueOf(v)
    if val.Kind() != reflect.Struct {
    fmt.Println("It is a struct")
    }
    }

If I call it with any struct as an argument it prints "This is a struct".
However, I won't be able to assign new values to stuff inside v by using val,
due to the value not being addressable. Working around by the following:

func Test(v interface{}) {
    val := reflect.ValueOf(&v).Elem()
    if val.Kind() != reflect.Struct {
        fmt.Println("This never get's printed!")
    }
}

According to the doc, I would assume, that by taking the '&' I use a pointer to v and by the call of Elem() I get the element it points to, therefore val.Kind() should still return the same thing. It doesn't. val.Kind() now is a reflect.Interface.

Is there a way of not having to go

valForTestingKind := reflect.ValueOf(v)
valForSettingNewValue := reflect.ValueOf(&v).Elem()

as this somehow feels wrong.

答案1

得分: 2

通过给nextval赋值,你打破了它与原始val的关联。相反,使用Set()方法。

nextval.Set(reflect.MakeMap(reflect.MapOf(KEY, ELEM)))

Set()在反射世界中相当于赋值。当然,你必须确保它是可赋值的,就像你在第一个代码示例中所做的那样,使用reflect.ValueOf(&v).Elem()


问题在于你有另一层间接性。v的类型是interface{},并且具有类型为Kind struct的具体值。就像每个接受interface类型参数的函数一样,当你调用reflect.ValueOf时,参数会自动转换为该类型。然而,将一个接口转换为另一个接口会导致具体值在新接口类型中重新封装。在重新封装之前的类型信息丢失了。例如,一个接受io.Writer的函数不会知道调用函数将其视为io.ReaderWriter。

在这个上下文中,这意味着reflect.ValueOf无法确定你是否传递了一个os.File(某个结构体)还是封装在interface{}中的文件。它假设你传递了一个os.File,并显示Kind为"struct"。

然而,当你传递一个指向interface{}的指针时,你传递的是一个可以修改的interface{}变量。你没有传递底层具体类型,这有重要的后果。你可以.Set()任何东西,不仅仅是原始具体类型允许的。你也不能编辑interface{}中的单个字段,因为它不可赋值。如果具体类型实际上是一个指针,你可以进行第四次解引用(.Elem())并从那里修改字段。

那么,从代码的角度来看,这意味着什么?

// let v = 具有SomeStruct具体类型的interface{}
val := reflect.ValueOf(&v).Elem()
fmt.Println(val.Elem().Kind())                // struct
val.Elem().Field(0).Set(10)                   // PANIC!字段不可赋值。
val.Set("一个不是SomeStruct的字符串")
fmt.Println(val.Elem().Kind())                // string

我在这里做了一个示例:http://play.golang.org/p/6MULn3KoNh

英文:

Part 1:

By assigning to nextval, you are breaking its association with the original val. Instead, use the Set() method.

nextval.Set(reflect.MakeMap(reflect.MapOf(KEY, ELEM)))

Set() is the equivalent of assignment in the reflection world. Of course, you must make sure it is assignable using reflect.ValueOf(&v).Elem() as you do in your first code example.


Part 2:

The issue here is that you have another level of indirection. v is of type interface{} and has a concrete value whose type is of Kind struct. Just like with every function that accepts an interface typed parameter, when you call reflect.ValueOf, the parameter is automatically converted to that type. However, converting an interface to another interface results in the concrete value being reboxed in the new interface type. The information of the type before it was reboxed is lost. As an example, a function that accepts an io.Writer would not know that the calling function considered it an io.ReaderWriter.

In this context, it means that reflect.ValueOf cannot tell if you passed an os.File (some struct) or a file boxed in an interface{}. It assumes you passed an os.File and shows you the Kind "struct".

However, when you pass a pointer to an interface{}, you are passing an interface{} variable that can be modified. You are not passing the underlying concrete type and that has important consequences. You can .Set() anything, not just what the original concrete type allows. You also can't edit individual fields as anything in an interface{} is not assignable. If the concrete type is in fact a pointer, you can do a fourth dereference (.Elem()) and modify fields from there.

So, what does this mean in terms of code?

//let v = an interface{} with a concrete type of SomeStruct
val := reflect.ValueOf(&v).Elem()
fmt.Println(val.Elem().Kind())                // struct
val.Elem().Field(0).Set(10)                   // PANIC! Field isn't assignable.
val.Set("a string which is not a SomeStruct")
fmt.Println(val.Elem().Kind())                // string

I made an example here: http://play.golang.org/p/6MULn3KoNh

答案2

得分: 1

我想谈谈你的第二段代码:

val := ... //将一个reflect.Value赋值给它
nextval := val.Field(0) //确保Field存在且为map类型
nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
fmt.Println(nextval.MapKeys()
fmt.Println(val.Field(index).MapKeys())

在第三行,你将一个新的、不同的对象重新赋值给变量nextval。你应该调用nextval的某种设置方法,而不是重新赋值它,这样可能会导致行为不同。在你的第一个例子中,你调用了SetString,但在这个例子中,你只是重新赋值了变量,这可能是行为不同的原因。在重新赋值变量之后,nextval将不再与val.Field(0)有任何关联。另外,index是什么?

如果这不能解释你的问题,请编辑问题,包含一个简短、自包含、正确、可编译的示例(SSCCE)。我希望能够将其发布到golang.org首页的文本框中以查看问题。尽可能在可能的情况下,始终发布一个SSCCE。

英文:

I want to talk about your second block of code:

val := ... //Assign a reflect.Value to it
nextval := val.Field(0) //Make sure that Field exists and is of type map
nextval = reflect.MakeMap(reflect.MapOf(KEY, ELEM))
nextval.SetMapIndex(Some_value_of_type_KEY, Something_of_type_ELEM)
fmt.Println(nextval.MapKeys()
fmt.Println(val.Field(index).MapKeys())

On the third line, you are reassigning a new, different object to the variable nextval. Shouldn't you call some kind of setting method on nextval instead of reassigning it? In your first example, you called SetString but in this example you are just reassigning the variable and that might be why the behavior is different. After you reassign the variable, nextval will no longer be connected in any way to val.Field(0). Also, what is index?

If this does not explain your problem, please edit the question to contain a short, self-contained, correct, compilable example ( SSCCE ). I want to be able to post it into the text box on the front page of golang.org in order to see the problem. You should always post an SSCCE when possible.

答案3

得分: 0

你没有展示一个完整且可编译的代码。你是传递一个指向结构体的指针还是按值传递结构体?如果是后者,反射无法修改它。

英文:

You have not shown a complete and compilable code. Do you pass a pointer to a struct or do you pass the struct by value? In the later case reflection cannot mutate it.

答案4

得分: 0

在使用反射时,即使不使用反射,存储在映射中的值也是不可寻址的。

http://play.golang.org/p/wYLeJ3W4R2

http://play.golang.org/p/ttUGBVh1lc

https://groups.google.com/forum/#!topic/golang-nuts/jzjEXoc9FwU

https://groups.google.com/forum/#!topic/golang-nuts/V_5kwzwKJAY

英文:

Values stored in a map are not addressable even when not using reflection.

http://play.golang.org/p/wYLeJ3W4R2

http://play.golang.org/p/ttUGBVh1lc

https://groups.google.com/forum/#!topic/golang-nuts/jzjEXoc9FwU

https://groups.google.com/forum/#!topic/golang-nuts/V_5kwzwKJAY

huangapple
  • 本文由 发表于 2013年6月30日 08:37:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/17385994.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定