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