简体   繁体   English

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

[英]How to access unexported struct fields

Is there a way to use reflect to access unexported fields in Go 1.8?有没有办法使用反射访问 Go 1.8 中的未导出字段? This no longer seems to work: https://stackoverflow.com/a/17982725/555493这似乎不再起作用: 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.请注意, reflect.DeepEqual工作得很好(也就是说,它可以访问未导出的字段),但我无法确定 function 的正面或反面。 Here's a go playarea that shows it in action: https://play.golang.org/p/vyEvay6eVG .这是一个 go 游戏区,展示了它的实际效果: https://play.golang.org/p/vyEvay6eVG The src code is below src代码如下

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
}

If the struct is addressable, you can use unsafe.Pointer to access the field (read or write ) it, like this:如果结构是可寻址的,您可以使用unsafe.Pointer访问该字段(读取或写入),如下所示:

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.根据文档unsafe.Pointer这种使用是有效的,并且运行go vet不会返回任何错误。

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.请参阅操场上的完整示例。

Based on cpcallen 's work:基于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 might be confusing to read at first. reflect.NewAt可能会reflect.NewAt感到困惑。 It returns a reflect.Value representing a pointer to a value of the specified field.Type() , using unsafe.Pointer(field.UnsafeAddr()) as that pointer.它返回一个reflect.Value表示指向指定field.Type()的值的指针,使用unsafe.Pointer(field.UnsafeAddr())作为该指针。 In this context reflect.NewAt is different than reflect.New , which would return a pointer to a freshly initialized value.在这种情况下, reflect.NewAtreflect.New不同,后者将返回一个指向新初始化值的指针。

Example:例子:

type Foo struct {
    unexportedField string
}

GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))

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

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()可以做到这一点,因为它可以访问reflect包的未导出功能,在这种情况下,即用于valueInterface()函数,该函数采用safe参数,拒绝通过Value.Interface()访问未导出的字段值Value.Interface()方法如果safe=true reflect.DeepEqual() will (might) call that passing safe=false . reflect.DeepEqual()将(可能)调用传递safe=false

You can still do it, but you cannot use Value.Interface() for unexported fields.您仍然可以这样做,但您不能将Value.Interface()用于未导出的字段。 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).相反,您必须使用特定于类型的方法,例如Value.String()表示stringValue.Float()表示浮点数, Value.Int()表示 int 等。这些将返回值的副本(这就足够了检查它),但不允许您修改字段的值(如果Value.Interface()可以工作并且字段类型是指针类型,这可能是“部分”可能的)。

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.如果字段恰好是接口类型,则可以使用Value.Elem()获取接口值包含/包装的值。

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 ):输出(在Go Playground上试试):

string hello
int64 2
float64 3
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)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM