go - 如何在 go 中获取私有(private) reflect.Value 的内容?

标签 go reflection

我正在尝试为复杂数据类型制作通用调试打印机,因为 %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

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

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

最佳答案

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

到达内在的唯一途径reflect.Value是调用Interface()并输入 assert 它,但如果在未导出的字段上调用它会 panic 。解决这个问题的唯一方法是使用 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在所有环境中都不允许或不可取,更不用说颠覆类型系统很少是正确的做法。建议您使此类代码以构建标签为条件,并包含一个安全的替代方案。

关于go - 如何在 go 中获取私有(private) reflect.Value 的内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60286882/

相关文章:

java - 使用反射和泛型时发出警告

go - 如何顺序运行处理程序函数

go - 如何在 Go 中声明一个不可变变量?

loops - 无限循环 : keep printing a single line

web - 如何在dart中获取当前调用函数的名称?

reflection - 远程处理:从服务器 AppDomain 查找客户端 AppDomain/程序集

java - 如何通过 ModelMapper 的 PropertyMap 使用继承

java - 内存中有文件系统库吗?

go - 如何调试 revel 构建崩溃日志?

Java 反射 : access private method inside inner class