Go語言提供了一種機(jī)制朋腋,在編譯時(shí)不知道類型的情況下,在運(yùn)行時(shí)膜楷,可更新變量旭咽、查看值、調(diào)用方法以及直接對(duì)它的結(jié)構(gòu)成員進(jìn)行操作赌厅,這種機(jī)制稱為反射(reflection)穷绵。
1、Go的反射程序包:reflect
反射功能由reflect包提供特愿,包內(nèi)定義了兩個(gè)重要的類型:Type和Value仲墨。
Type表示Go語 言的一個(gè)類型,它是一個(gè)有很多方法的接口揍障,這些方法可以用來識(shí)別類型以及透視類型的組 成部分目养,比如一個(gè)結(jié)構(gòu)的各個(gè)字段或者一個(gè)函數(shù)的各個(gè)參數(shù)。reflect.Type接口只有一個(gè)實(shí)現(xiàn)毒嫡,即類型描述符癌蚁,接口值中的動(dòng)態(tài)類型也是類型描述符。
reflect.Value可以包含一任意類型的值。
通過示例展示Type和Value的使用
package main
import (
"fmt"
"reflect" //引入反射包
)
// 定義用戶結(jié)構(gòu)
type User struct {
Id int
Name string
Age int
}
// 定義方法
func (u User)Hello(){
fmt.Println("Hello World!")
}
func main() {
u := User{1,"Tom",12}
// Info(&u) 傳入指針類型努释,編譯報(bào)錯(cuò):panic: reflect: NumField of non-struct type
Info(u) // 通過值拷貝輸入對(duì)象
}
// 反射示例函數(shù)
func Info(o interface{}) {
t := reflect.TypeOf(o) // 獲取類型數(shù)據(jù)
fmt.Println("Type:", t.Name()) // 打印類型名稱
v := reflect.ValueOf(o) // 獲取對(duì)象的數(shù)值
fmt.Println("Fields:")
for i :=0; i < t.NumField(); i++ {
f := t.Field(i) // 獲取指定索引結(jié)構(gòu)成員的信息
val := v.Field(i).Interface() // 獲取指定結(jié)構(gòu)成員的值
fmt.Printf("%6s: %v = %v\n", f.Name, f.Type, val) // 打印成員的名稱碘梢,類型和值
}
for i :=0; i <t.NumMethod(); i++ {
m := t.Method(i) // 獲取指定索引的方法的信息
fmt.Printf("%6s: %v\n", m.Name, m.Type) // 打印方法的名稱,方法聲明
}
}
程序運(yùn)行輸出的結(jié)果如下圖:
2伐蒂、匿名字段的反射
在Go語言中煞躬,可以通過索引來獲取匿名字段的信息,參見程序代碼:
package main
import (
"fmt"
"reflect" //引入反射包
)
// 定義用戶結(jié)構(gòu)
type User struct {
Id int
Name string
Age int
}
type Manager struct {
User // 定義匿名字段
title string
}
func main() {
m := Manager{User: User{1, "Lisa", 28}, title: "CFO"} // 對(duì)象定義及匿名User類型字段初始化
t := reflect.TypeOf(m) // 獲取對(duì)象的類型信息
fmt.Printf("%#v\n", t.Field(0)) // 通過索引獲取匿名字段的信息
fmt.Printf("%#v\n", t.Field(1)) // 獲取title字段的信息
// 獲取匿名User類型字段成員的方法逸邦,通過傳入slice來制定索引對(duì)應(yīng)的成員恩沛,可以獲取相應(yīng)的信息
fmt.Printf("%#v\n", t.FieldByIndex([]int{0,0})) //獲取User中的Id成員
fmt.Printf("%#v\n", t.FieldByIndex([]int{0,1})) //獲取User中的Name成員
}
運(yùn)行輸出結(jié)果:
上圖中,可以看到昭雌,對(duì)于User類型的匿名成員的名稱為:User复唤,是否匿名Anonymous標(biāo)記為:true,而對(duì)于User成員Id和Name來講烛卧,匿名標(biāo)記為false佛纫。
3、通過反射修改結(jié)構(gòu)對(duì)象的值
package main
import (
"fmt"
"reflect" //引入反射包
)
// 定義用戶結(jié)構(gòu)
type User struct {
Id int
Name string
Age int
}
func main() {
u := User{1,"Tom", 28}
fmt.Println("before:",u)
Set(&u)
fmt.Println("after:",u)
}
func Set(o interface{}) {
v := reflect.ValueOf(o)
if v.Kind() == reflect.Ptr && !v.Elem().CanSet() {
fmt.Print("輸入的對(duì)象是不可修改的")
} else {
v = v.Elem()
}
// 判斷是否獲取到制定名稱的字段信息
f := v.FieldByName("Name")
if !f.IsValid() {
fmt.Println("沒有找到Name的字段")
return
}
// 修改字段的數(shù)值
if f.Kind() == reflect.String {
f.SetString("Tom2")
}
}
運(yùn)行結(jié)果如下:
reflect 包中還有很多有用的方法总放,具體詳見包的參考文檔及源碼