go中的reflect.ValueOf()和Value.Elem()有什么区别? [英] What is the difference between reflect.ValueOf() and Value.Elem() in go?
问题描述
几天前,我开始学习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()
是一个功能,可以将其视为反射的切入点.当您具有非反射"值(例如string
或int
)时,可以使用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屋!