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