【问题标题】:How do I get at the contents of a private reflect.Value in go?如何在 go 中获取私有 reflect.Value 的内容?
【发布时间】:2020-02-18 17:57:49
【问题描述】:

我正在尝试为复杂数据类型制作通用调试打印机,因为%v 倾向于只打印指针值而不是它们指向的内容。在我必须处理包含 reflect.Value 字段的结构之前,我已经让它与所有东西一起工作。

以下演示代码运行无误:(https://play.golang.org/p/qvdRKc40R8k)

package main

import (
    "fmt"
    "reflect"
)

type MyStruct struct {
    i int
    R reflect.Value
}

func printContents(value interface{}) {
    // Omitted: check if value is actually a struct
    rv := reflect.ValueOf(value)
    for i := 0; i < rv.NumField(); i++ {
        fmt.Printf("%v: ", rv.Type().Field(i).Name)
        field := rv.Field(i)
        switch field.Kind() {
        case reflect.Int:
            fmt.Printf("%v", field.Int())
        case reflect.Struct:
            // Omitted: check if field is actually a reflect.Value to an int
            fmt.Printf("reflect.Value(%v)", field.Interface().(reflect.Value).Int())
        }
        fmt.Printf("\n")
    }
}

func main() {
    printContents(MyStruct{123, reflect.ValueOf(456)})
}

打印出来:

i: 123
R: reflect.Value(456)

但是,如果我将 MyStruct 的 R 字段名称更改为 r,则会失败:

panic: reflect.Value.Interface: cannot return value obtained from unexported field or method

当然,失败是正确的,因为否则这将是一种将未出口的田地进入适当的戈兰的方法,这是一个禁忌。

但这让我陷入了困境:如何在不使用 Interface() 的情况下访问未导出的 reflect.Value 所指的任何内容,以便我可以浏览其内容并打印?我浏览了反射文档,没有发现任何看起来有用的东西......

【问题讨论】:

标签: go reflection


【解决方案1】:

经过一番挖掘,我找到了解决方案:

获取内部reflect.Value 的唯一方法是调用Interface() 并键入 assert 它,但是如果在未导出的字段上调用它会出现恐慌。解决这个问题的唯一方法是使用unsafe 包清除只读标志,以便Interface() 方法认为它不是导出的(基本上,我们颠覆了类型系统):

type flag uintptr // reflect/value.go:flag

type flagROTester struct {
    A   int
    a   int // reflect/value.go:flagStickyRO
    int     // reflect/value.go:flagEmbedRO
    // Note: flagRO = flagStickyRO | flagEmbedRO
}

var flagOffset uintptr
var maskFlagRO flag
var hasExpectedReflectStruct bool

func initUnsafe() {
    if field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag"); ok {
        flagOffset = field.Offset
    } else {
        log.Println("go-describe: exposeInterface() is disabled because the " +
            "reflect.Value struct no longer has a flag field. Please open an " +
            "issue at https://github.com/kstenerud/go-describe/issues")
        hasExpectedReflectStruct = false
        return
    }

    rv := reflect.ValueOf(flagROTester{})
    getFlag := func(v reflect.Value, name string) flag {
        return flag(reflect.ValueOf(v.FieldByName(name)).FieldByName("flag").Uint())
    }
    flagRO := (getFlag(rv, "a") | getFlag(rv, "int")) ^ getFlag(rv, "A")
    maskFlagRO = ^flagRO

    if flagRO == 0 {
        log.Println("go-describe: exposeInterface() is disabled because the " +
            "reflect flag type no longer has a flagEmbedRO or flagStickyRO bit. " +
            "Please open an issue at https://github.com/kstenerud/go-describe/issues")
        hasExpectedReflectStruct = false
        return
    }

    hasExpectedReflectStruct = true
}

func canExposeInterface() bool {
    return hasExpectedReflectStruct
}

func exposeInterface(v reflect.Value) interface{} {
    pFlag := (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + flagOffset))
    *pFlag &= maskFlagRO
    return v.Interface()
}

需要注意的是,unsafe 在所有环境中都是不允许的或不可取的,更不用说颠覆类型系统很少是正确的做法。建议您使此类代码以构建标签为条件,并包含一个安全的替代方案。

【讨论】:

    猜你喜欢
    • 2018-06-14
    • 2021-01-21
    • 2013-08-08
    • 1970-01-01
    • 2016-10-15
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    相关资源
    最近更新 更多