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

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
}
  • 写回答

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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 如何增强飞上天的树莓派的热点信号强度,以使得笔记本可以在地面实现远程桌面连接
  • ¥15 MCNP里如何定义多个源?
  • ¥20 双层网络上信息-疾病传播
  • ¥50 paddlepaddle pinn
  • ¥20 idea运行测试代码报错问题
  • ¥15 网络监控:网络故障告警通知
  • ¥15 django项目运行报编码错误
  • ¥15 请问这个是什么意思?
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services