Edited at

exportされていないフィールドの値を変更する #golang

More than 1 year has passed since last update.


公開されていないフィールドの値を変更する

以下のように、フィールドに対応するreflect.Valueのアドレスを元に、reflect.NewAtreflect.Valueを作り直してやれば、reflect.CanSettrueを返すようになり、セットできるようになります。

なお、unsafeを使っているので、Google App Engineでは使えません。

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

package main

import (
"fmt"
"reflect"
"unsafe"
)

type hoge struct {
n int
}

func main() {
h := &hoge{n: 100}
v := reflect.ValueOf(h)
fv1 := v.Elem().FieldByName("n")
fmt.Println(fv1.CanSet())

fv2 := reflect.NewAt(fv1.Type(), unsafe.Pointer(fv1.UnsafeAddr()))
if fv2.Elem().CanSet() {
fv2.Elem().SetInt(200)
}
fmt.Println(h)
}


型がわかっている場合

追記(2018/01/19):社内から指摘をうけました!

型がわかっている場合は、reflect.NewAtを使わずにできます。

https://play.golang.org/p/iF6RLTw-TXe

package main

import (
"fmt"
"reflect"
"unsafe"
)

type hoge struct {
n int
}

func main() {
h := &hoge{n: 100}
v := reflect.ValueOf(h)
fv1 := v.Elem().FieldByName("n")
i := (*int)(unsafe.Pointer(fv1.UnsafeAddr()))
*i = 200
fmt.Println(*i)
}


メソッドはどうなのか

以下のように、メソッドは、reflect.CanAddrfalseを返すので、reflect.NewAtが使えないのでダメです。

https://play.golang.org/p/9F9E3CtvMvF

package main

import (
"fmt"
"reflect"
"unsafe"
)

type hoge struct {
n int
}

func (h hoge) N() int {
return h.n
}

func main() {
h := &hoge{n: 100}
v := reflect.ValueOf(h)
mv1 := v.Elem().MethodByName("N")
fmt.Println(mv1.CanSet())
fmt.Println(mv1.CanAddr()) // false

mv2 := reflect.NewAt(mv1.Type(), unsafe.Pointer(mv1.UnsafeAddr()))
if mv2.Elem().CanSet() {
mv2.Elem().Set(reflect.ValueOf(func() int {
return 200
}))
}
fmt.Println(h.N())
}