go中reflect.ValueOf()和Value.Elem()之间有什么区别?

go中reflect.ValueOf()和Value.Elem()之间有什么区别?,go,pointers,reflection,interface,Go,Pointers,Reflection,Interface,几天前,我开始学习golang,我发现这是一个令人困惑的问题。这两个函数/方法之间的区别是什么?如何正确使用它们 两个函数/方法都返回一个值,并根据go doc ValueOf返回初始化为接口i中存储的具体值的新值。ValueOf(nil)返回零值 Elem返回接口v包含的值或指针v指向的值。如果v的种类不是接口或Ptr,它就会恐慌。如果v为nil,则返回零值 我在stackoverflow上的一篇文章中找到了这段代码,但仍然不知道何时使用.Elem() func设置字段(obj接口{},名称字

几天前,我开始学习golang,我发现这是一个令人困惑的问题。这两个函数/方法之间的区别是什么?如何正确使用它们

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

ValueOf返回初始化为接口i中存储的具体值的新值。ValueOf(nil)返回零值

Elem返回接口v包含的值或指针v指向的值。如果v的种类不是接口或Ptr,它就会恐慌。如果v为nil,则返回零值

我在stackoverflow上的一篇文章中找到了这段代码,但仍然不知道何时使用.Elem()

func设置字段(obj接口{},名称字符串,值接口{})错误{
//如果我删除.Elem()将不起作用
structValue:=reflect.ValueOf(obj.Elem())
structFieldValue:=structValue.FieldByName(名称)
if!structFieldValue.IsValid(){
返回fmt.Errorf(“对象中没有此类字段:%s”,名称)
}
if!structFieldValue.CanSet(){
返回fmt.Errorf(“无法设置%s字段值”,名称)
}
structFieldType:=structFieldValue.Type()
//如果在末尾添加.Elem(),也不会起作用
val:=反映。ValueOf(值)
如果structFieldType!=val.Type(){
返回fmt.Errorf(“提供的值%v类型%v与对象字段类型%v不匹配”,val,val.type(),structFieldType)
}
structFieldValue.Set(val)
归零
}
是一个函数,可以将其视为反射的入口点。当您有一个“非反射”值时,例如
字符串
int
,您可以使用
reflect.ValueOf()
获取它的描述符

是一种反映.Value的
方法。因此,只有当您已经有一个
reflect.Value
时,才能使用此选项。您可以使用
Value.Elem()
获取原始
reflect.Value
包装的值所指向的值(
reflect.Value
)。请注意,您也可以使用。对于
Value.Elem()
,还有另一个“用例”,但它更“高级”,我们在答案的末尾返回到它

要“离开”反射,可以使用常规方法,该方法将包装的值作为
接口{}
返回给您

例如:

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
这将输出(在上尝试):

有关Go的反思的精彩介绍,请阅读。虽然如果你只是从围棋开始,我会把注意力集中在其他事情上,并把思考留给以后的冒险

Value.Elem()的另一个用例
这是一个高级的话题,所以如果你不理解它,不要惊慌失措。你不需要

我们了解了在
reflect.Value
中包装指针时如何使用
Value.Elem()
进行“导航”。
Value.Elem()的文档说明:

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

因此,如果
reflect.Value
包装了一个接口值,
Value.Elem()
也可以用来获得包装在该接口值中的具体值

Go中的接口是它自己的主题,对于内部,您可以阅读Russ Cox。同样,对于围棋初学者来说,这不一定是一个话题

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

所以,如果您向接口传递一个指针,这个指针将被包装在
interface{}
值中。您可以使用
Value.Elem()
获取指向的值,该值将是一个接口值(不是具体值),再次使用
Value.Elem()
将获得具体值

这个例子说明了这一点:

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

在.

上尝试一下。在我看来,reflect.ValueOf()返回的值类似于包含所有必要元数据的文件头,而Value.Elem()返回的值则是实际的文件体?@stevenh6140不,不是真的。在我的第一个例子中,我有一个
I
变量,它是
int
类型,还有一个
p
变量,它指向
int
类型,指向
I
。如果您在
p
reflect.Value
上调用
Elem()
,您将得到一个
reflect.Value
,如果您首先在
i
上调用
reflect.ValueOf()
,您将得到一个
reflect.Value
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