マスター・オブ・Reflectパッケージ

5,707 views

Published on

GoCon 2013 Autumnで発表した資料です。

Published in: Technology
1 Comment
28 Likes
Statistics
Notes
No Downloads
Views
Total views
5,707
On SlideShare
0
From Embeds
0
Number of Embeds
621
Actions
Shares
0
Downloads
23
Comments
1
Likes
28
Embeds 0
No embeds

No notes for slide

マスター・オブ・Reflectパッケージ

  1. 1. マスター・オブ ・reflectパッケージ 2013/10/14 Mon
  2. 2. 自己紹介 KLab株式会社 エンジニア 上田拓也 主に使用する言語 趣味:Golang, Java, 仕事:PHP, JavaScript twitter : @tenntenn
  3. 3. 今日話すことは... ● Go研 Vol.8でやったことをまとめたもの ○ https://github. com/goken/goken/blob/master/goken08-reflect.md
  4. 4. アジェンダ ● なぜ、reflectパッケージを使うのか? ● 実際どこで使われているの? ● Value型とType型 ● interface{}とreflectパッケージ ● 構造体のリフレクション ● チャネルのリフレクション ● 関数のリフレクション
  5. 5. なぜ、reflectパッケージを使うのか?
  6. 6. 動機1: Genericsがない ● こういう事はできない func foo(in <T>) <T> {//...} func main() { n := 100 m := foo(n) }
  7. 7. 動機2:型の情報を取りたい ● 構造体にはタグとしてフィールド情報を埋め込 める → リフレクションで取り出す type Person struct { Name string `json:”name”` Age } int `json:”age”` →タグ
  8. 8. 実際どこで使われているの?
  9. 9. encoding/{json, xml, ...}パッケージ ● JSONなどのシリアライズされたものを構造体に 変換する際に使用される Go言語の構造体: JSON: type Person struct { Name string `json:”name”` Aget int `json:”age”` } { “name”: “Gopher”, “age”: 4 } タグを使って対応付けがされる!
  10. 10. {text, html}/templateパッケージ ● HTMLなどに任意の型の値を埋め込むために 使われる Hello, {{.Name}}!! Execute Hello, Gopher!! テンプレート 出力 データの埋込み Person { Name: “Gopher”, Age: 4, }
  11. 11. Value型とType型
  12. 12. Value型とType型 ● Value型 ○ 値を表す型 ○ 全ての型の値は、reflectパッケージの世界では、Value 型で表される ● Type型 ○ 型を表す型 ○ 全ての型は、reflectパッケージの世界では、Type型で 表される
  13. 13. Value型の定義 type Value struct { typ *rtype // 型 val unsafe.Pointer // 値へのポインタ flag // メタ情報 }
  14. 14. Value型の取得とKind ● reflect.ValueOfを使う v := reflect.Value(100) ● v.Kind()で値の種類がとれる Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer
  15. 15. Type型の定義 ● インタフェース型 ● メソッドやフィールドの情報を取るメソッドがある ● Value型が持っているメソッドに近いものを持っ ている
  16. 16. Type型の実体 ● Type型の実体は*rtype型である ● *rtype型がType型を実装している type Type interface{ // …. common() *rtype uncommon() *uncommonType }
  17. 17. Type型の取得 ● reflect.TypeOf()かValue.Type()で取得できる t1 := reflect.TypeOf(100) v := reflect.ValueOf(100) t2 := v.Type() ● reflect.Newを使えばオブジェクトを作れる typ := reflect.TypeOf(100) obj := reflect.New(typ)
  18. 18. interface{}とreflectパッケージ
  19. 19. 変数に値を設定する ● ポインタじゃないと設定できない n := 100 v1 := reflect.ValueOf(n) fmt.Println(v1.CanSet()) // false=設定 v2 := reflect.ValueOf(&n) v2.Elem().SetInt(200) fmt.Println(n) // 200 Elemでポインタの 指してる先を取得
  20. 20. 変数に値を設定する n: 100 &n ValueOf() Value (ポインタ) Elem() Set Value (Int)
  21. 21. interface{}としてポインタを渡す ● interface{}として、任意の型のポインタを受け取 る func set(p, v interface{}) { pv := reflect.ValueOf(p) // ポインタ vv := reflect.ValueOf(v) // 設定する値 pv.Elem().Set(vv) }
  22. 22. 構造体のリフレクション
  23. 23. フィールドの情報を取得する(Value) type MyStruct struct { field1 string field2 MyStruct2 } type MyStruct2 struct { field int } ms := MyStruct{ // ms.field1 fmt.Println(v.Field(0)) // ms.field2.field fmt.Println(v. FieldByIndex([]int{1, 0})) "str", MyStruct2{100}, } v := reflect.ValueOf(ms) // ms.field1 fmt.Println(v. FieldByName("field1"))
  24. 24. フィールドの情報を取得する(Value) type MyStruct struct { field1 string field2 MyStruct2 } type MyStruct2 struct { field int } ms := MyStruct{ "str", MyStruct2{100}, } v := reflect.ValueOf(ms) f := func(name string) bool { return name == "field1" } fmt.Println(v.FieldByNameFunc (f)) fmt.Println(v.NumField())
  25. 25. Value型から取得したフィールド ● ポインタ経由でないとSetできない ● privateなフィールドにはSetできない type Hoge struct { N int } func main() { h := Hoge{10} hpv := reflect.ValueOf(&h) hpv.Elem().FieldByName("N").SetInt(200) fmt.Println(h) }
  26. 26. フィールドの情報を取得する(Type) ● タグを取得する type Hoge struct { N int `json:"n"` } type StructTag string func main() { h := Hoge{10} t := reflect.TypeOf(h) n, _ := t.FieldByName("N") fmt.Println(n.Tag.Get("json")) }
  27. 27. メソッドの情報の取得 ● フィールドと同じようにValue型とType型のオブ ジェクトから情報がとれる ● Value.Call()でメソッドを呼ぶ事ができる
  28. 28. まとめ 何か値 interface{} reflect.ValueOf() Value .Elem().Set() Value.XXX() 何かの変数の ポインタ Value reflect.ValueOf() interface{}
  29. 29. 残りは、時間があったらやる
  30. 30. チャネルのリフレクション
  31. 31. チャネルのリフレクション ● reflect.MakeChan ○ チャネルを作れる ● Value.Send() ○ チャネルにデータを送る ● Value.Recv() ○ チャネルからデータを受取る ● Value.TrySend(), Value.TryRecv() ○ ブロックしないバージョン ● Value.Len() ○ バッファ内のデータの数 ● Value.Cap() ○ バッファの容量
  32. 32. reflect.Select func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) ● 引数 ○ cases: caseの配列 ● 戻り値 ○ chosen: 選んだcaseのインデックス ○ recv: 受取ったチャネル ○ recvOK: 受取れたか
  33. 33. SelectCase type SelectCase struct { Dir SelectDir // チャネルの方向 Chan Value Send Value } // Valueにしたチャネル // 送信する値
  34. 34. SelectDir type SelectDir int const ( SelectSend // case Chan <- Send SelectRecv // case <-Chan: SelectDefault // default )
  35. 35. 関数のリフレクション
  36. 36. Callする f := func(n int) { fmt.Println(n) } fv := reflect.ValueOf(f) fmt.Println(fv) // func (v Value) Call(in []Value) []Value fv.Call([]reflect.Value{reflect.ValueOf(100)})
  37. 37. 関数の情報をType型から取得する ● Type.NumIn() int ○ 引数の数 ● Type.In(i int) Type ○ i番目の引数の型を取得する ● Type.NumOut() int ○ 戻り値の数 ● Type.Out(i int) Type ○ i番目の戻り値の型を取得する
  38. 38. 関数を作る func(in []Value) []Value reflect.MakeFunc() Value Elem().Set() Value &func(...)... reflect.ValueOf()
  39. 39. 関数を作る - 合成関数を作る func main() { f := func(x int) int { return x * x } var g func(x int) int // 2つの関数を受け取り、第3引数の変数に入れる Compose(f, f, &g) fmt.Println(g(2)) // 16 }
  40. 40. 関数を作る func Compose(f, g, fptr interface{}) { fv := reflect.ValueOf(f) gv := reflect.ValueOf(g) fgv := func(in []reflect.Value) []reflect.Value { return gv.Call(fv.Call(in)) } fn := reflect.ValueOf(fptr).Elem() v := reflect.MakeFunc(fn.Type(), fgv) fn.Set(v) }
  41. 41. まとめ 何か値 interface{} reflect.ValueOf() Value .Elem().Set() Value.XXX() 何かの変数の ポインタ Value reflect.ValueOf() interface{}

×