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

January 18, 2018

この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。

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

以下のように、フィールドに対応する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())
}