dongshang1979
2019-08-07 07:02
浏览 1.9k
已采纳

go中的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?

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

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

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.

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
}

图片转代码服务由CSDN问答提供 功能建议

几天前,我开始学习golang并发现 reflect.Valueof() Value.Elem()非常令人困惑。 这两个函数/方法之间的区别是什么以及如何正确使用它们?

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

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

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

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

  func SetField(obj interface {},name string,value interface {})错误{
 
 //不会 如果我删除.Elem()
 structValue:= reflect.ValueOf(obj).Elem()
 
 structFieldValue:= structValue.FieldByName(name)
 
如果!structFieldValue.IsValid(){
 返回fmt.Errorf(“没有这样的字段:obj中的%s”,名称)
} 
 
,如果!structFieldValue.CanSet(){
返回fmt.Errorf(“无法设置%s字段值”,名称 )
} 
 
 structFieldType:= structFieldValue.Type()
 
 //如果将.Elem()添加到末尾也将不起作用
 val:= reflect.ValueOf(value)
 如果structFieldType!= val.Type(){
 
返回fmt.Errorf(“提供的值%v类型%v与obj字段类型%v不匹配”,val,val.Type(),structFieldType)
  } 
 
 structFieldValue.Set(val)
恢复 rn nil 
} 
   
 
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • donglu2761 2019-08-07 07:06
    已采纳

    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() 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.

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

    For example:

    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
    

    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.

    Another use case for Value.Elem()

    This is kind of an advanced topic, so don't freak out if you don't understand it. You don't need to.

    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 returns the value that the interface v contains or that the pointer v points to.

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

    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.

    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!).

    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
    

    Try it on the Go Playground.

    打赏 评论

相关推荐 更多相似问题