公開されていないフィールドの値を変更する
以下のように、フィールドに対応するreflect.Value
のアドレスを元に、reflect.NewAt
でreflect.Value
を作り直してやれば、reflect.CanSet
がtrue
を返すようになり、セットできるようになります。
なお、unsafe
を使っているので、Google App Engineでは使えません。
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
を使わずにできます。
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.CanAddr
がfalse
を返すので、reflect.NewAt
が使えないのでダメです。
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())
}