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

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.

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

报告相同问题?

悬赏问题

  • ¥15 cplex运行后参数报错是为什么
  • ¥15 之前不小心删了pycharm的文件,后面重新安装之后软件打不开了
  • ¥15 vue3获取动态宽度,刷新后动态宽度值为0
  • ¥15 升腾威讯云桌面V2.0.0摄像头问题
  • ¥15 关于Python的会计设计
  • ¥15 聚类分析 设计k-均值算法分类器,对一组二维模式向量进行分类。
  • ¥15 stm32c8t6工程,使用hal库
  • ¥15 找能接spark如图片的,可议价
  • ¥15 关于#单片机#的问题,请各位专家解答!
  • ¥15 博通raid 的写入速度很高也很低