Top Banner
マスター・オブ・ 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
21

マスター・オブ・reflectパッケージ II

Apr 16, 2017

Download

Technology

Takuya Ueda
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: マスター・オブ・reflectパッケージ II

マスター・オブ・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

Page 2: マスター・オブ・reflectパッケージ II

アジェンダ

■ 自己紹介

■ 基礎編● reflectパッケージとは?

● Value型とType型

● 変数に値を入れる

● 構造体を触る

■ 応用編● 任意の型にメソッドを生やしたい● 任意個のチャネルに対してselectしたい● 実行時に任意の型を作りたい

■ まとめ

2

Page 3: マスター・オブ・reflectパッケージ II

自己紹介

KLab株式会社KLabGames事業本部 エンジニア

@六本木

上田拓也twitter: @tenntenn■ 好きな言語

Go, JavaScript, Lua

■ 業務

モバイルオンラインゲームの開発:Unity, Lua

■ 最近はまってること

英会話

3

Page 4: マスター・オブ・reflectパッケージ II
Page 5: マスター・オブ・reflectパッケージ II

基礎編

5

Page 6: マスター・オブ・reflectパッケージ II

reflectパッケージとは?

■ 何ができるのか?

● 実行時に型情報を取得

● 任意の型の変数に値を入れる

● 構造体のフィールドのタグを取得する

■ どこで使われてるの?● encodingパッケージ● ORマッパーなど

■ なんで使うの?● ジェネリクスがない● ただ、ただ楽しい!!!

基礎編 6

Page 7: マスター・オブ・reflectパッケージ II

Value型とType型

■ Value型● 任意の値を表す型

● 値への操作をメソッドで提供

● reflect.ValueOf()で取得できる

■ Type型● 任意の型を表す型● 型に関する操作をメソッドで提供

● reflect.TypeOf()で取得できる

基礎編 7

Page 8: マスター・オブ・reflectパッケージ II

変数に値を入れる基礎編 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

Page 9: マスター・オブ・reflectパッケージ II

構造体を触る基礎編 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

Page 10: マスター・オブ・reflectパッケージ II

応用編

10

Page 11: マスター・オブ・reflectパッケージ II

任意の型にメソッドを生やしたい 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

Page 12: マスター・オブ・reflectパッケージ II

任意の型にメソッドを生やしたい 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

Page 13: マスター・オブ・reflectパッケージ II

任意の型にメソッドを生やしたい 3■ メソッドのValue値へ値を設定できるか?

● できない!

応用編 13

n := MyInt(100)v := reflect.ValueOf(&n).Elem()fv := v.MethodByName("Int")

// falseprintln(fv.CanSet())

http://play.golang.org/p/yXFpnucbPw

Page 14: マスター・オブ・reflectパッケージ II

任意の型にメソッドを生やしたい 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の振る舞いを変える

Page 15: マスター・オブ・reflectパッケージ II

任意個のチャネルに対してselectしたい 1

■ select● 複数チャネルの受信/送信を行う

● コンパイル時に対象のチャネル数は決定

応用編 15

select {case n := <-ch1:println(n)

case ch2 <- 100:default:println("Defualt")

}

Page 16: マスター・オブ・reflectパッケージ II

任意個のチャネルに対して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

Page 17: マスター・オブ・reflectパッケージ II

実行時に任意の型を作りたい 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の挙動だけ変える

Page 18: マスター・オブ・reflectパッケージ II

実行時に任意の型を作りたい 2■ そんなに現実は甘くない

応用編 18

$ go run main.goboolpanic: interface conversion: reflect.Type is main.MyType, not *reflect.rtype

エクスポートされてない!

Page 19: マスター・オブ・reflectパッケージ II

実行時に任意の型を作りたい 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が起きる

Page 20: マスター・オブ・reflectパッケージ II

まとめ

■ reflectパッケージは楽しくて強力

● 動的に色々できる○ StructTagの取得

○ 任意個のSelectCase

■ 使うときは慎重に

● reflectしかできないこと?

● 自動コード生成でどうにかならない?

● インタフェースで十分では?

● パフォーマンスは?

20

Page 21: マスター・オブ・reflectパッケージ II