golang reflect
go語言中reflect反射機制。詳細原文:地址
接口值到反射對象
package main
import (
"fmt"
"reflect"
)
func main() {
var x int = 1
fmt.Println("type: ", reflect.TypeOf(x))
}
type: int
TypeOf
函數的定義如下,參數為接口類型,返回值為類型
func TypeOf(i interface {}) Type
ValueOf
函數的定義如下,參數為接口類型,返回值為Value
var x int = 1
fmt.Println("value: ", reflect.ValueOf(x))
value: <int Value>
可以通過Kind
函數來檢查類型,
fmt.Println("Kind: ", reflect.ValueOf(x).Kind())
fmt.Println("Kind is Int? ", reflect.ValueOf(x).Kind() == reflect.int)
Kind: int
Kind is Int? true
反射對象到接口值
通過Interface
函數可以實現反射對象到接口值的轉換,
func (v Value) Interface() interface {}
// Interface 以 interface{} 返回 v 的值
y := v.Interface().(float64)
fmt.Println(y)
修改反射對象
修改反射對象的前提條件是其值必須是可設置的
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.3) // Error: panic
為了避免這個問題,需要使用CanSet
函數來檢查該值的設置性,
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v: ", v.CanSet())
settability of v: false
那么如何才能設置該值呢?
這里需要考慮一個常見的問題,參數傳遞
,傳值還是傳引用或地址?
在上面的例子中,我們使用的是reflect.ValueOf(x)
,這是一個值傳遞,傳遞的是x的值的一個副本,不是x本身,因此更新副本中的值是不允許的。如果使用 reflect.ValueOf(&x)
來替換剛才的值傳遞,就可以實現值的修改。
var x float64 = 3.4
p := reflect.ValueOf(&x) // 獲取x的地址
fmt.Println("settability of p: ", p.CanSet())
v := p.Elem()
fmt.Println("settability of v: ", v.CanSet())
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
settability of p: false
settability of v: true
7.1
7.1
獲取結構體標簽
首先介紹如何遍歷結構體字段內容,
假設結構體如下,
type T struct {
A int
B string
}
t := T{12, "skidoo"}
從而,通過反射來遍歷所有的字段內容
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
}
0 A int = 23
1 B string = skidoo
接下來,如何獲取結構體的標簽內容?
func main() {
type S struct {
F string `species:"gopher" color:"blue"`
}
s := S{}
st := reflect.TypeOf(s)
field := st.Field(0)
fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))
}
interface{}到函數反射
一般情況下,為了存儲多個函數值,一般采用map
來存儲。其中key為函數名稱,而value為相應的處理函數。
在這里需要定義好函數類型,但是函數的參數以及返回類型就需要是統一的,如下
package main
import "fmt"
func say(text string) {
fmt.Println(text)
}
func main() {
var funcMap = make(map[string]func(string))
funcMap["say"] = say
funcMap["say"]("hello")
}
如果希望map
可以存儲任意類型的函數(參數不同,返回值不同),那么就需要用interface{}而不是func(param...)來定義value。
package main
import "fmt"
func say(text string) {
fmt.Println(text)
}
func main() {
var funcMap = make(map[string]interface{})
funcMap["say"] = say
funcMap["say"]("hello")
}
cannot call non-function funcMap["say"] (type interface {})
直接調用會報錯,提示不能調用interface{}類型的函數。
這時,需要利用reflect
把函數從interface轉換到函數來使用,
package main
import (
"fmt"
"reflect"
)
func say(text string) {
fmt.Println(text)
}
func Call(m map[string]interface{}, name string, params ... interface{}) (result []reflect.Value) {
f := reflect.ValueOf(m[name])
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
result = f.Call(in)
return
}
func main() {
var funcMap = make(map[string]interface{})
funcMap["say"] = say
Call(funcMap, "say", "hello")