go中的reflect.ValueOf()和Value.Elem()有什么区别? [英] What is the difference between reflect.ValueOf() and Value.Elem() in go?

查看:888
本文介绍了go中的reflect.ValueOf()和Value.Elem()有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

几天前,我开始学习golang,发现 reflect.Valueof() Value.Elem()相当令人困惑.这两种功能/方法有什么区别,以及如何正确使用它们?

I started learning golang a couple of days ago and found reflect.Valueof() and Value.Elem() quite confusing. What is the difference between this two function/methods and how to use them correctly?

函数/方法都返回一个值,并根据go doc

Both function/methods return a Value, and according to the go doc

ValueOf返回一个新的Value,并将其初始化为存储在接口i中的具体值. ValueOf(nil)返回零值.

ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.

Elem返回接口v包含的值或指针v指向的值.如果v的Kind不是Interface或Ptr,它会感到恐慌.如果v为零,则返回零值.

Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil.

我从stackoverflow上的帖子中找到了这段代码,但仍然不知道何时使用.Elem()

I found this code from a post on stackoverflow but still don't understand when to use .Elem()

func SetField(obj interface{}, name string, value interface{}) error {
    
    // won't work if I remove .Elem()
    structValue := reflect.ValueOf(obj).Elem()
    
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()

    // won't work either if I add .Elem() to the end
    val := reflect.ValueOf(value)
    if structFieldType != val.Type() {

        return fmt.Errorf("Provided value %v type %v didn't match obj field type %v",val,val.Type(),structFieldType)
    }

    structFieldValue.Set(val)
    return nil
}

推荐答案

reflect.ValueOf() 是一个功能,可以将其视为反射的切入点.当您具有非反射"值(例如stringint)时,可以使用reflect.ValueOf()来获取

reflect.ValueOf() is a function, think of it as the entry point to reflection. When you have a "non-reflection" value, such as a string or int, you can use reflect.ValueOf() to get a reflect.Value descriptor of it.

Value.Elem() 方法reflect.Value.因此,只有在已有reflect.Value的情况下,才可以使用它.您可以使用Value.Elem()来获取由原始reflect.Value包装的值所指向的值(reflect.Value).请注意,您也可以为此使用 reflect.Indirect() . Value.Elem()还有另一个用例",但是更高级",我们在答案的最后返回.

Value.Elem() is a method of reflect.Value. So you can only use this if you already have a reflect.Value. You may use Value.Elem() to get the value (reflect.Value) pointed by the value wrapped by the original reflect.Value. Note that you may also use reflect.Indirect() for this. There's another "use case" for Value.Elem(), but it's more "advanced", we return to it at the end of the answer.

要保留"反射,可以使用常规的 Value.Interface() 方法,它以interface{}的形式返回包装后的值.

To "leave" reflection, you may use the general Value.Interface() method, which returns you the wrapped value as an interface{}.

例如:

var i int = 3
var p *int = &i
fmt.Println(p, i)

v := reflect.ValueOf(p)
fmt.Println(v.Interface()) // This is the p pointer

v2 := v.Elem()
fmt.Println(v2.Interface()) // This is i's value: 3

这将输出(在转到游乐场上尝试):

This will output (try it on the Go Playground):

0x414020 3
0x414020
3

要全面了解Go的反思,请阅读 Go博客:反思法则.尽管如果您只是从Go开始,我会专注于其他事情,并为以后的冒险留下思考.

For a great introduction to Go's reflection, read The Go Blog: The Laws of Reflection. Although if you're just starting with Go, I'd focus on other things and leave reflection for a later adventure.

这是一个高级主题,因此,如果您不了解它,请不要惊慌.您不需要.

我们看到了当指针包裹在reflect.Value中时,如何使用Value.Elem()来导航". Value.Elem()的文档说:

We saw how Value.Elem() can be used to "navigate" when a pointer is wrapped in the reflect.Value. Doc of Value.Elem() says:

Elem返回接口v包含的值或指针v指向的值.

Elem returns the value that the interface v contains or that the pointer v points to.

因此,如果reflect.Value包装接口值,则Value.Elem()也可以用于获取包装在该接口值中的具体值.

So if reflect.Value wraps an interface value, Value.Elem() may also be used to get the concrete value wrapped in that interface value.

Go接口是它自己的主题,对于内部结构,您可以阅读 Go数据结构:接口由拉斯·考克斯(Russ Cox)撰写.同样,不一定是Go入门者的主题.

Interfaces in Go is its own topic, for the internals, you may read Go Data Structures: Interfaces by Russ Cox. Again, not necessarily a topic for Go starters.

基本上,无论您传递给reflect.ValueOf()的值是什么,如果它还不是接口值,它将被隐式包装在interface{}中.如果传递的值已经是接口值,则存储在其中的具体值将作为interface{}传递.如果您将指针传递给接口,则会出现第二个用例"(否则,在Go!中非常罕见).

Basically whatever value you pass to reflect.ValueOf(), if it's not already an interface value, it will be wrapped in an interface{} implicitly. If the passed value is already an interface value, then the concrete value stored in it will be passed as a interface{}. This second "use case" surfaces if you pass a pointer to interface (which is otherwise very rare in Go!).

因此,如果将指针传递给接口,则该指针将包装在interface{}值中.您可以使用Value.Elem()获取指向的值,该值将是接口值(而不是具体值),并且再次使用Value.Elem() 可以得到具体值.

So if you pass a pointer to interface, this pointer will be wrapped in an interface{} value. You may use Value.Elem() to get the pointed value, which will be an interface value (not a concrete value), and using Value.Elem() again on this will give you the concrete value.

此示例对此进行了说明:

This example illustrates it:

var r io.Reader = os.Stdin // os.Stdin is of type *os.File which implements io.Reader

v := reflect.ValueOf(r) // r is interface wrapping *os.File value
fmt.Println(v.Type())   // *os.File

v2 := reflect.ValueOf(&r)            // pointer passed, will be wrapped in interface{}
fmt.Println(v2.Type())               // *io.Reader
fmt.Println(v2.Elem().Type())        // navigate to pointed: io.Reader (interface type)
fmt.Println(v2.Elem().Elem().Type()) // 2nd Elem(): get concrete value in interface: *os.File

去游乐场上尝试.

这篇关于go中的reflect.ValueOf()和Value.Elem()有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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