当前位置: 首页 > 面试题库 >

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

常乐
2023-03-14
问题内容

我几天前开始学习golang,发现reflect.Valueof()和Value.Elem()非常令人困惑。这两种功能/方法有什么区别,以及如何正确使用它们?

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

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

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

我从帖子中找到了此代码,但仍然不知道何时使用.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
}

问题答案:

reflect.ValueOf()是一个 函数
,请将其视为反射的切入点。当您具有“非反射”值(例如string或)时int,可以reflect.ValueOf()用来获取其reflect.Value描述符。

Value.Elem()是一个 方法
reflect.Value。因此,只有在您已有时,才可以使用它reflect.Value。您可以Value.Elem()用来获取reflect.Value由原始包装的值所指向的值()reflect.Value。请注意,您也可以使用reflect.Indirect()它。还有一个“用例”
Value.Elem(),但是它更“高级”,我们在答案的最后返回。

要“离开”反射,可以使用常规Value.Interface()方法,该方法将包装的值作为返回interface{}

例如:

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 Playground上尝试):

0x414020 3
0x414020
3

有关Go反射的重要介绍,请阅读The Go Blog:反射法则。尽管如果您只是从Go开始,我会专注于其他事情,并为以后的冒险留神。

另一个用例 Value.Elem()

这是一个高级主题,所以如果您不了解它,请不要惊慌。 不用了

我们看到了Value.Elem()当指针包装在时如何用于“导航” reflect.Value。的医生Value.Elem()说:

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

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

Go中的接口是它自己的主题,对于内部结构而言,您可以阅读Russ
Cox的Go数据结构:接口。再次重申,不一定是Go入门者的主题。

基本上,无论您传递给什么值reflect.ValueOf(),如果它还不是接口值,它将被interface{}隐式包装。如果传递的值已经是接口值,则存储在其中的具体值将作为传递interface{}。如果您将指针传递给接口,则会出现第二个“用例”(否则,在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

在Go Playground上尝试一下。



 类似资料:
  • 问题内容: 我是Go编程语言的新手。 我注意到Go中有一些奇怪的地方:我认为它在Python中使用并替代了,但是当我在Go中使用时,它也是可行的。 和之间有什么区别? 问题答案: 是分配。有关Go中分配的更多信息:分配 和在变量声明中使用时的细微差别。 Go中变量声明的一般形式是: 上面的声明创建一个特定类型的变量,为其添加名称,并设置其初始值。 无论是或可以省略,但不能同时使用。 例如: 被称为

  • 问题内容: 使用该工具一段时间后,它看起来像: (可选)下载, 编译, 并安装 一个软件,而仅仅是 编译 并安装 它。在这种情况下,为什么该命令被取代而存在? 问题答案: 是本地工作时工作流程的一部分。假设您要使用库,但是由于某些原因需要进行更改。您可以这样做: ,仅下载该文件; 对下载的软件包进行更改; 安装本地版本。 据我知道有没有标志,以表明它应该 不 下载,所以它不能代替在这里。 从头开始

  • 问题内容: 和Go 之间有什么区别? 什么时候使用“他”或“她”? 为什么? 你好 输出是,而缺少,有时我觉得很奇怪。 问题答案: 并且是不同的类型,但是它们可以相互转换: 3。将一个字节的切片转换为字符串类型会产生一个字符串,其连续字节是该切片的元素。 4。将字符串类型的值转换为字节片的类型会产生一个片,其连续元素是字符串的字节。 博客:数组,切片(和字符串):’append’的机制: 字符串实

  • 问题内容: 我一直认为Java 中的运算符用于验证其两个布尔操作数是否均为,并且该&运算符用于对两种整数类型进行按位运算。 最近我知道,也可以使用运算符来验证其两个布尔操作数是否均为,唯一的区别是即使LHS操作数为false,它也会检查RHS操作数。 Java中的运算符是否在内部重载?还是在这背后有其他概念? 问题答案: <-验证两个操作数 <-停止评估第一个操作数是否为false,因为结果为fa

  • 问题内容: JavaScript中的&和&&有什么区别? 示例代码: 似乎&&是一个逻辑上的“和”,如果两个都为true,则总是为我提供第二个值。 但是什么是&? (顺便说一下,&&在Python中似乎是“ and”;&在Python中似乎是&。) 问题答案: 是按位AND 该运算符期望 两个数字 并重新调整一个 数字。 如果它们不是数字,则将其强制转换为数字。 注意: 在Javascript中,

  • 问题内容: 我正在从Spring 2.5迁移到Spring 3。 他们介绍了一些黑魔法。预期仅在servlet配置文件中声明。 在Spring 2.5中,我刚刚使用,并且在分发服务器servlet配置XML中声明了标记,并使用了要扫描的基本包。 所以,我不知道是什么样的区别,并在servlet配置标签和我有什么可以消除在Spring3配置文件? 问题答案: 声明一般注释,比如支持,等等。 声明了注