英文:
How to access unexported struct fields
问题
在Go 1.8中,可以使用反射(reflect)来访问未导出的字段吗?
这个方法似乎不再起作用:https://stackoverflow.com/a/17982725/555493
请注意,reflect.DeepEqual
(深度比较函数)可以正常工作(即可以访问未导出的字段),但我对该函数的实现原理一头雾水。以下是一个演示它工作原理的Go Playground链接:https://play.golang.org/p/vyEvay6eVG。下面是源代码:
import (
"fmt"
"reflect"
)
type Foo struct {
private string
}
func main() {
x := Foo{"hello"}
y := Foo{"goodbye"}
z := Foo{"hello"}
fmt.Println(reflect.DeepEqual(x, y)) // false
fmt.Println(reflect.DeepEqual(x, z)) // true
}
英文:
Is there a way to use reflect to access unexported fields in Go 1.8?
This no longer seems to work: https://stackoverflow.com/a/17982725/555493
Note that reflect.DeepEqual
works just fine (that is, it can access unexported fields) but I can't make heads or tails of that function. Here's a go playarea that shows it in action: https://play.golang.org/p/vyEvay6eVG. The src code is below
import (
"fmt"
"reflect"
)
type Foo struct {
private string
}
func main() {
x := Foo{"hello"}
y := Foo{"goodbye"}
z := Foo{"hello"}
fmt.Println(reflect.DeepEqual(x,y)) //false
fmt.Println(reflect.DeepEqual(x,z)) //true
}
答案1
得分: 56
如果结构体是可寻址的,你可以使用unsafe.Pointer
来访问该字段(读取或写入),像这样:
rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf 无法读取或设置。
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// 现在 rf 可以被读取和设置。
根据文档,使用unsafe.Pointer
是有效的,并且运行go vet
不会返回错误。
如果结构体不可寻址,这个技巧就不起作用,但你可以创建一个可寻址的副本,像这样:
rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// 现在 rf 可以被读取。设置操作会成功,但只会影响临时副本。
英文:
If the struct is addressable, you can use unsafe.Pointer
to access the field (read or write) it, like this:
rs := reflect.ValueOf(&MyStruct).Elem()
rf := rs.Field(n)
// rf can't be read or set.
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read and set.
See full example on the playground.
This use of unsafe.Pointer
is valid according to the documentation and running go vet
returns no errors.
If the struct is not addressable this trick won't work, but you can create an addressable copy like this:
rs = reflect.ValueOf(MyStruct)
rs2 := reflect.New(rs.Type()).Elem()
rs2.Set(rs)
rf = rs2.Field(0)
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
// Now rf can be read. Setting will succeed but only affects the temporary copy.
答案2
得分: 23
根据cpcallen的工作:
import (
"reflect"
"unsafe"
)
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
reflect.NewAt
可能一开始看起来有些困惑。它返回一个reflect.Value
,该值表示指向指定field.Type()
的值的指针,使用unsafe.Pointer(field.UnsafeAddr())
作为该指针。在这个上下文中,reflect.NewAt
与reflect.New
不同,后者会返回一个指向新初始化值的指针。
示例:
type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
https://play.golang.org/p/IgjlQPYdKFR
英文:
Based on cpcallen's work:
import (
"reflect"
"unsafe"
)
func GetUnexportedField(field reflect.Value) interface{} {
return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Interface()
}
func SetUnexportedField(field reflect.Value, value interface{}) {
reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).
Elem().
Set(reflect.ValueOf(value))
}
reflect.NewAt
might be confusing to read at first. It returns a reflect.Value
representing a pointer to a value of the specified field.Type()
, using unsafe.Pointer(field.UnsafeAddr())
as that pointer. In this context reflect.NewAt
is different than reflect.New
, which would return a pointer to a freshly initialized value.
Example:
type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
答案3
得分: 6
reflect.DeepEqual()
可以做到这一点,因为它可以访问reflect
包中未导出的特性,这里特指valueInterface()
函数。该函数接受一个safe
参数,如果safe=true
,则通过Value.Interface()
方法拒绝访问未导出的字段值。reflect.DeepEqual()
可能会调用该函数,并传递safe=false
。
你仍然可以做到这一点,但是不能使用Value.Interface()
来访问未导出的字段。相反,你必须使用特定于类型的方法,例如对于字符串使用Value.String()
,对于浮点数使用Value.Float()
,对于整数使用Value.Int()
等。这些方法将返回值的副本(足以进行检查),但不允许修改字段的值(如果Value.Interface()
可以工作并且字段类型是指针类型,则可能“部分”可行)。
如果字段恰好是接口类型,你可以使用Value.Elem()
来获取接口值所包含/封装的值。
示例代码如下:
type Foo struct {
s string
i int
j interface{}
}
func main() {
x := Foo{"hello", 2, 3.0}
v := reflect.ValueOf(x)
s := v.FieldByName("s")
fmt.Printf("%T %v\n", s.String(), s.String())
i := v.FieldByName("i")
fmt.Printf("%T %v\n", i.Int(), i.Int())
j := v.FieldByName("j").Elem()
fmt.Printf("%T %v\n", j.Float(), j.Float())
}
输出结果(在Go Playground上尝试):
string hello
int64 2
float64 3
英文:
reflect.DeepEqual()
can do it because it has access to unexported features of the reflect
package, in this case namely for the valueInterface()
function, which takes a safe
argument, which denies access to unexported field values via the Value.Interface()
method if safe=true
. reflect.DeepEqual()
will (might) call that passing safe=false
.
You can still do it, but you cannot use Value.Interface()
for unexported fields. Instead you have to use type-specific methods, such as Value.String()
for string
, Value.Float()
for floats, Value.Int()
for ints etc. These will return you a copy of the value (which is enough to inspect it), but will not allow you to modify the field's value (which might be "partly" possible if Value.Interface()
would work and the field type would be a pointer type).
If a field happens to be an interface type, you may use Value.Elem()
to get to the value contained / wrapped by the interface value.
To demonstrate:
type Foo struct {
s string
i int
j interface{}
}
func main() {
x := Foo{"hello", 2, 3.0}
v := reflect.ValueOf(x)
s := v.FieldByName("s")
fmt.Printf("%T %v\n", s.String(), s.String())
i := v.FieldByName("i")
fmt.Printf("%T %v\n", i.Int(), i.Int())
j := v.FieldByName("j").Elem()
fmt.Printf("%T %v\n", j.Float(), j.Float())
}
Output (try it on the Go Playground):
string hello
int64 2
float64 3
答案4
得分: 0
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type Person1 struct {
W3ID string
Name string
}
type Address1 struct {
city string
country string
}
type User1 struct {
name string
age int
address Address1
manager Person1
developer Person1
tech Person1
}
func showDetails(load, email interface{}) {
if reflect.ValueOf(load).Kind() == reflect.Struct {
typ := reflect.TypeOf(load)
value := reflect.ValueOf(load)
//#1 For struct, not addressable create a copy With Element.
value2 := reflect.New(value.Type()).Elem()
//#2 Value2 is addressable and can be set
value2.Set(value)
for i := 0; i < typ.NumField(); i++ {
if value.Field(i).Kind() == reflect.Struct {
rf := value2.Field(i)
/* #nosec G103 */
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
irf := rf.Interface()
typrf := reflect.TypeOf(irf)
nameP := typrf.String()
if strings.Contains(nameP, "Person") {
//fmt.Println(nameP, "FOUND !!!!!!! ")
for j := 0; j < typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, "W3ID") {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, " MATCH!!!!")
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf("%d.Type:%T || Value:%#v\n",
// (i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := "tsumi@in.org.com"
load := User1{
name: "John Doe",
age: 34,
address: Address1{
city: "New York",
country: "USA",
},
manager: Person1{
W3ID: "jBult@in.org.com",
Name: "Bualt",
},
developer: Person1{
W3ID: "tsumi@in.org.com",
Name: "Sumi",
},
tech: Person1{
W3ID: "lPaul@in.org.com",
Name: "Paul",
},
}
showDetails(load, iD)
}
英文:
package main
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type Person1 struct {
W3ID string
Name string
}
type Address1 struct {
city string
country string
}
type User1 struct {
name string
age int
address Address1
manager Person1
developer Person1
tech Person1
}
func showDetails(load, email interface{}) {
if reflect.ValueOf(load).Kind() == reflect.Struct {
typ := reflect.TypeOf(load)
value := reflect.ValueOf(load)
//#1 For struct, not addressable create a copy With Element.
value2 := reflect.New(value.Type()).Elem()
//#2 Value2 is addressable and can be set
value2.Set(value)
for i := 0; i < typ.NumField(); i++ {
if value.Field(i).Kind() == reflect.Struct {
rf := value2.Field(i)
/* #nosec G103 */
rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
irf := rf.Interface()
typrf := reflect.TypeOf(irf)
nameP := typrf.String()
if strings.Contains(nameP, "Person") {
//fmt.Println(nameP, "FOUND !!!!!!! ")
for j := 0; j < typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, "W3ID") {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, " MATCH!!!!")
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf("%d.Type:%T || Value:%#v\n",
// (i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := "tsumi@in.org.com"
load := User1{
name: "John Doe",
age: 34,
address: Address1{
city: "New York",
country: "USA",
},
manager: Person1{
W3ID: "jBult@in.org.com",
Name: "Bualt",
},
developer: Person1{
W3ID: "tsumi@in.org.com",
Name: "Sumi",
},
tech: Person1{
W3ID: "lPaul@in.org.com",
Name: "Paul",
},
}
showDetails(load, iD)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论