プログラミング言語Goを読みながらメモ(第十二章)
プログラミング言語 Go を読みながらメモ。
第一章 : https://qiita.com/Nabetani/items/077c6b4d3d1ce0a2c3fd
第二章 : https://qiita.com/Nabetani/items/d053304698dfa3601116
第三章 : https://qiita.com/Nabetani/items/2fd9c372fcd8383955a5
第四章 : https://qiita.com/Nabetani/items/59bfd00dc3323883a07f
第五章 : https://qiita.com/Nabetani/items/4b785f1c9b0b26d48475
第六章 : https://qiita.com/Nabetani/items/1c100394a65af6506187
第七章 : https://qiita.com/Nabetani/items/6553ad253af77661e915
第八章 : https://qiita.com/Nabetani/items/3b2e3964159cc292fe00
第九章 : https://qiita.com/Nabetani/items/c94e51aa29926a56159e
で。
第十章と第十一章は、特にメモしなかったので省略。
そして。
第十二章は、楽しいリフレクション。
構造体の中身を調べる
package main
import (
"fmt"
"reflect"
)
func fields(t reflect.Type) []interface{} {
if t.Kind() != reflect.Struct {
return []interface{}{"not struct"}
}
numf := t.NumField()
var names = []interface{}{}
for n := 0; n < numf; n++ {
f := t.Field(n)
if f.Type.Kind() == reflect.Struct {
names = append(names, fields(f.Type))
} else {
names = append(names, fmt.Sprintf("%q %v\n", f.Name, f.Type))
}
}
return names
}
func main() {
v0 := struct {
foo bool
bar int
baz []string
hoge struct {
fuga int
piyo [12]string
}
}{}
fmt.Println(fields(reflect.TypeOf(v0)))
}
を実行すると
["foo" bool
"bar" int
"baz" []string
["fuga" int
"piyo" [12]string
]]
が出力される。
よしよし。
構造体じゃないものに対して NumField
を呼んだり、Slice, Array, String でないものに対して Index
を呼んだりするとパニックらしい。
CanAddr() と CanSet()
package main
import (
"fmt"
"reflect"
)
func main() {
fmt.Println(reflect.ValueOf(2).CanAddr()) // false
x := 2
fmt.Println(reflect.ValueOf(x).CanAddr()) // false
fmt.Println(reflect.ValueOf(&x).CanAddr()) // false
fmt.Println(reflect.ValueOf(&x).Elem().CanAddr()) // true
y := []string{"foo", "bar", "baz"}
fmt.Println(reflect.ValueOf(y[0]).CanAddr()) // false
fmt.Println(reflect.ValueOf(y).Index(0).CanAddr()) // true
}
こんな具合。なるほど。
CanAddr()
ならアドレスが取れる。
package main
import (
"fmt"
"reflect"
)
func main() {
x := 2
xptr := reflect.ValueOf(&x).Elem().Addr().Interface().(*int)
fmt.Println(*xptr) //=> 2
*xptr = 123
fmt.Println(x) //=> 123
y := []string{"foo", "bar", "baz"}
yzero := reflect.ValueOf(y).Index(0).Addr().Interface().(*string)
fmt.Println(*yzero) //=>foo
*yzero = "FOO"
fmt.Println(y) //=> [FOO bar baz]
}
アドレスが取れなくても、 SetInt
などで設定できる:
package main
import (
"fmt"
"reflect"
)
func main() {
x := 2
xv := reflect.ValueOf(&x).Elem()
xv.SetInt(123)
fmt.Println(x) //=> 123
y := []string{"foo", "bar", "baz"}
y0v := reflect.ValueOf(y).Index(0)
y0v.SetString("FOO")
fmt.Println(y) //=> [FOO bar baz]
}
しかし、CanAddr()
であっても、CanSet()
ではない場合は Set できない。
package main
import (
"fmt"
"os"
"reflect"
)
func main() {
stdout := os.Stdout
fmt.Printf("%T\n", stdout) //=> *os.File
vs := reflect.ValueOf(stdout).Elem()
file := vs.FieldByName("file")
pfd := file.Elem().FieldByName("pfd")
sysfd := pfd.FieldByName("Sysfd")
fmt.Printf("Sysfd = %v\n", sysfd) //=> Sysfd = 1
fmt.Printf("addr:%v set:%v\n", sysfd.CanAddr(), sysfd.CanSet()) //=> addr:true set:false
fmt.Printf("Sysfd = %v\n", sysfd.Addr().Interface())
// panic reflect.Value.Interface: cannot return value obtained from unexported field or method
}
Set もなにも、 Interface を取ろうとするだけで panic になる。
出来ないこと
Ruby と違って実行時に構造体を定義したりは出来ないっぽい。
当たり前のような、そうでもないような。
まあもし作れても、リフレクション以外の方法でアクセスできないのであまり意味はないけどね。