マスター・オブ・reflectパッケージ II
2016/04/17(日)@第3回 関西golang勉強会
The Go gopher was designed by Renee French.The gopher stickers was made by Takuya Ueda.Licensed under the Creative Commons 3.0 Attributions license.
参考:マスター・オブ・ reflectパッケージ Go研 Vol.8
アジェンダ
■ 自己紹介
■ 基礎編● reflectパッケージとは?
● Value型とType型
● 変数に値を入れる
● 構造体を触る
■ 応用編● 任意の型にメソッドを生やしたい● 任意個のチャネルに対してselectしたい● 実行時に任意の型を作りたい
■ まとめ
2
自己紹介
KLab株式会社KLabGames事業本部 エンジニア
@六本木
上田拓也twitter: @tenntenn■ 好きな言語
Go, JavaScript, Lua
■ 業務
モバイルオンラインゲームの開発:Unity, Lua
■ 最近はまってること
英会話
3
reflectパッケージとは?
■ 何ができるのか?
● 実行時に型情報を取得
● 任意の型の変数に値を入れる
● 構造体のフィールドのタグを取得する
■ どこで使われてるの?● encodingパッケージ● ORマッパーなど
■ なんで使うの?● ジェネリクスがない● ただ、ただ楽しい!!!
基礎編 6
Value型とType型
■ Value型● 任意の値を表す型
● 値への操作をメソッドで提供
● reflect.ValueOf()で取得できる
■ Type型● 任意の型を表す型● 型に関する操作をメソッドで提供
● reflect.TypeOf()で取得できる
基礎編 7
変数に値を入れる基礎編 8
var n intfmt.Println(n) // 0
vp := reflect.ValueOf(&n)v := vp.Elem()if v.CanSet() {v.SetInt(100)
}
fmt.Println(n) // 100http://play.golang.org/p/HkJPjQsP8o
構造体を触る基礎編 9
s := struct{A string `k:"v"`; b int
}{"a", 1}
v := reflect.ValueOf(&s).Elem() println(v.FieldByName("A").CanSet())println(v.FieldByName("b").CanSet())
f1, ok := v.Type().FieldByName("A")println(ok, f1.PkgPath, f1.Tag.Get("k"))
f2, _ := v.Type().FieldByName("b")println(f2.PkgPath)
http://play.golang.org/p/NkwP3KSjDu
任意の型にメソッドを生やしたい 1■ メソッド
● パッケージ内の型をレシーバにできる
● メソッド値として扱える
応用編 11
type MyInt intfunc (n MyInt) Int() int {
return int(n)}
func main() {f := MyInt(100).Intprintln(f())
}
http://play.golang.org/p/b4p60PpJtj
任意の型にメソッドを生やしたい 2■ reflectで関数を作る
● MakeFuncを使う
応用編 12
func Compose(f, g, fptr interface{}) {fv, gv := reflect.ValueOf(f), reflect.ValueOf(g)
fgv := func(in []reflect.Value) []reflect.Value {return gv.Call(fv.Call(in))
}
out := reflect.ValueOf(fptr).Elem()v := reflect.MakeFunc(out.Type(), fgv)out.Set(v)
}
http://play.golang.org/p/_bNy3H2575
任意の型にメソッドを生やしたい 3■ メソッドのValue値へ値を設定できるか?
● できない!
応用編 13
n := MyInt(100)v := reflect.ValueOf(&n).Elem()fv := v.MethodByName("Int")
// falseprintln(fv.CanSet())
http://play.golang.org/p/yXFpnucbPw
任意の型にメソッドを生やしたい 4■ メソッドの振る舞いを動的に変える
● インタフェースを埋め込む
応用編 14
type Hoge interface{M()}type fuga struct {Hoge}
func (f fuga) M() {fmt.Println("Hi")f.Hoge.M() // 元のメソッドを呼ぶ
}func HiHoge(h Hoge) Hoge {return fuga{h} // 構造体作る
}
Mの振る舞いを変える
任意個のチャネルに対してselectしたい 1
■ select● 複数チャネルの受信/送信を行う
● コンパイル時に対象のチャネル数は決定
応用編 15
select {case n := <-ch1:println(n)
case ch2 <- 100:default:println("Defualt")
}
任意個のチャネルに対してselectしたい 2
■ reflectでselectする
● reflect.Selectを使う
● 任意の個数のSelectCaseを渡せる
応用編 16
ch1, ch2 := make(chan int), make(chan int)go func() {ch1 <- 100}()go func() {<-ch2}()
i, v, ok := reflect.Select([]reflect.SelectCase{// case: <-ch1{reflect.SelectRecv, reflect.ValueOf(ch1), reflect.ValueOf(nil)},// case: ch2 <- 100{reflect.SelectSend, reflect.ValueOf(ch2), reflect.ValueOf(100)},// default:{reflect.SelectDefault, reflect.ValueOf(nil), reflect.ValueOf(nil)},
})fmt.Println(i, v, ok)
http://play.golang.org/p/7t3awR7EZC
実行時に任意の型を作りたい 1■ Type型
● 実はインタフェース
● reflect.New(typ Type) Value○ 任意の型の値を生成できる
応用編 17
type MyType struct {reflect.Type}func (typ MyType) Kind() reflect.Kind {
return reflect.Bool}
t:= MyType{reflect.TypeOf(0)}println(t.Kind()) // boolv := reflect.New(t)
Kindの挙動だけ変える
実行時に任意の型を作りたい 2■ そんなに現実は甘くない
応用編 18
$ go run main.goboolpanic: interface conversion: reflect.Type is main.MyType, not *reflect.rtype
エクスポートされてない!
実行時に任意の型を作りたい 3■ Type型の実態
● *rtype型がType型を実装している
■ New関数の実装
応用編 19
func New(typ Type) Value {if typ == nil {
panic("reflect: New(nil)")}
ptr := unsafe_New(typ.(*rtype))fl := flag(Ptr)
return Value{typ.common().ptrTo(), ptr, fl}}
ここでpanicが起きる
まとめ
■ reflectパッケージは楽しくて強力
● 動的に色々できる○ StructTagの取得
○ 任意個のSelectCase
■ 使うときは慎重に
● reflectしかできないこと?
● 自動コード生成でどうにかならない?
● インタフェースで十分では?
● パフォーマンスは?
20
Top Related