リフレクトを使用してgo 1.8でエクスポートされていないフィールドにアクセスする方法はありますか?これはもはや機能していないようです: https://stackoverflow.com/a/17982725/55549
ご了承ください reflect.DeepEqual
は問題なく動作します(つまり、エクスポートされていないフィールドにアクセスできます)が、その関数の先頭または末尾を作成することはできません。実際の動作を示すgo playareaは次のとおりです https://play.golang.org/p/vyEvay6eVG 。 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
}
構造体がアドレス可能な場合、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.
unsafe.Pointer
のこの使用法は、unsafe
パッケージのドキュメントおよびgo vet
sのドキュメントによると「有効」です。
構造体がアドレス可能でない場合、このトリックは機能しませんが、次のようにアドレス可能なコピーを作成できます。
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.
reflect.DeepEqual()
は、それが reflect
パッケージのエクスポートされていない機能にアクセスできるため、これを行うことができます。この場合、つまりvalueInterface()
関数は、safe
引数を受け取り、エクスポートされていないものへのアクセスを拒否します Value.Interface()
メソッドのフィールド値(safe=true
の場合)。 reflect.DeepEqual()
は、safe=false
を渡すことを呼び出す可能性があります。
引き続き実行できますが、エクスポートされていないフィールドにValue.Interface()
を使用することはできません。代わりに、型固有のメソッドを使用する必要があります。たとえば、string
には Value.String()
、浮動小数点数には Value.Float()
、intには 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
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
_は最初は読みにくいかもしれません。 field.Type()
をそのポインターとして使用して、指定されたunsafe.Pointer(field.UnsafeAddr())
の値へのポインターを表す_reflect.Value
_を返します。このコンテキストでは、_reflect.NewAt
_は_reflect.New
_とは異なり、新しく初期化された値へのポインタを返します。
例:
_type Foo struct {
unexportedField string
}
GetUnexportedField(reflect.ValueOf(&Foo{}).Elem().FieldByName("unexportedField"))
_