如何访问未导出的结构字段

huangapple go评论90阅读模式
英文:

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 可以被读取和设置。

在 playground 上查看完整示例

根据文档,使用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 可以被读取。设置操作会成功,但只会影响临时副本。

在 playground 上查看完整示例

英文:

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.

See full example on the playground.

答案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.NewAtreflect.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"))

https://play.golang.org/p/IgjlQPYdKFR

答案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 (
&quot;fmt&quot;
&quot;reflect&quot;
&quot;strings&quot;
&quot;unsafe&quot;
)
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 &lt; 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, &quot;Person&quot;) {
//fmt.Println(nameP, &quot;FOUND !!!!!!! &quot;)
for j := 0; j &lt; typrf.NumField(); j++ {
re := rf.Field(j)
nameW := typrf.Field(j).Name
if strings.Contains(nameW, &quot;W3ID&quot;) {
valueW := re.Interface()
fetchEmail := valueW.(string)
if fetchEmail == email {
fmt.Println(fetchEmail, &quot; MATCH!!!!&quot;)
}
}
}
}
showDetails(irf, email)
} else {
// fmt.Printf(&quot;%d.Type:%T || Value:%#v\n&quot;,
// 	(i + 1), value.Field(i), value.Field(i))
}
}
}
}
func main() {
iD := &quot;tsumi@in.org.com&quot;
load := User1{
name: &quot;John Doe&quot;,
age:  34,
address: Address1{
city:    &quot;New York&quot;,
country: &quot;USA&quot;,
},
manager: Person1{
W3ID: &quot;jBult@in.org.com&quot;,
Name: &quot;Bualt&quot;,
},
developer: Person1{
W3ID: &quot;tsumi@in.org.com&quot;,
Name: &quot;Sumi&quot;,
},
tech: Person1{
W3ID: &quot;lPaul@in.org.com&quot;,
Name: &quot;Paul&quot;,
},
}
showDetails(load, iD)
}

huangapple
  • 本文由 发表于 2017年3月8日 14:55:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/42664837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定