Go 語言的反射機制
因為沒有強類型語言的經(jīng)驗,反射這個概念,之前確實沒怎么接觸過。在維基百科上搜了一下,具體解釋如下:
在計算機學中,反射式編程(英語:reflective programming)或反射(英語:reflection),是指計算機程序在運行時(runtime)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為。
go 中的反射也是這種作用,可以在程序運行期間,獲取變量的類型與值的信息,然后進行訪問或或者修改。go 語言中,內(nèi)置了 reflect 包,用來獲取一個變量的類型(type)與值(value),對應的方法分別為 reflect.TypeOf() 和 reflect.ValueOf()。
反射類型
TypeOf 方法,會返回該變量的類型對象,類型對象下可以獲取到變量的類型與種類。
import?(
?"fmt"
?"reflect"
)
func?main()?{
?//?定義一個int類型的變量
?var?i?int?=?1
?//?獲取變量的類型對象
?var?typeOfNum?=?reflect.TypeOf(i)?
??//?輸出類型與種類
??typeOfNumName?=?typeOfNum.Name()
??typeOfNumKind?=?typeOfNum.Kind()
??fmt.Printf("name:?%s,?kind:?%s",?typeOfNumName,?typeOfNumKind)
}
可以看到,此時的類型與種類都為 int。

類型與種類
類型表示定義變量的時候指定的類型,可以反映 type 關鍵字定義的類型,而種類是變量最終歸屬的類型。說起來可能比較蒼白,我們直接上代碼。
type?num?int
//?定義一個num類型的變量
var?i?num?=?1
var?typeOfNum?=?reflect.TypeOf(i)?
可以看到,此時的類型為 num,種類為 int。

對于一些引用類型的變量,比如切片、函數(shù)、結(jié)構(gòu)體,kind 都能準確反映其底層的類型。
func?printTypeOf(typeOf?reflect.Type)?{
?fmt.Printf("name:?%s,?kind:?%s\n",?typeOf.Name(),?typeOf.Kind())
}
type?Person?struct?{}
type?IntSlice?[]int
func?main()?{
?var?a?=?IntSlice{}
?var?b?=?Person{}
?printTypeOf(reflect.TypeOf(a))
?printTypeOf(reflect.TypeOf(b))
}

而面對匿名結(jié)構(gòu)體或者匿名函數(shù),其類型值會返回為空。
func?main()?{
?var?a?=?struct?{}{}
?printTypeOf(reflect.TypeOf(a))
}

反射值
ValueOf 方法,可以獲取一個變量的值。
var?i?=?3.1415926
var?s?=?"歡迎關注我的公眾號:『自然醒的筆記本』"
fmt.Println(reflect.ValueOf(s))
fmt.Println(reflect.ValueOf(i))

通過反射的值對象,也能取到變量的種類,并且還能根據(jù)其種類,調(diào)用對應的方法獲取變量的真實值。
var?i?=?100
var?v?=?reflect.ValueOf(i)
fmt.Println(v.Int())?//?如果值是?Int?類型,可以通過?Int?方法獲取具體值
fmt.Println(v.Kind())

修改值
通過反射得到的值對象,可以對變量本身的值進行修改。首先,在獲取反射值時,不能直接獲取變量的反射值,而是要先取其指針的值對象。
var?i?=?100
var?v?=?reflect.ValueOf(&i)?//?取出變量i的指針的值對象
fmt.Println(v.Kind(),?v)
取出指針的值對象之后,不能立即賦值,因為此時拿到的是變量的地址。

要賦值的話,需要先調(diào)用 Elem 方法,取出具體元素,然后進行賦值。
var?i?=?100
var?v?=?reflect.ValueOf(&i)?//?取出變量i的指針的值對象
var?e?=?v.Elem()
e.SetInt(500)?//?修改元素值
fmt.Println(e.Kind(),?i)

值對象與結(jié)構(gòu)體
前面介紹過,通過反射可以得到變量的值,對于結(jié)構(gòu)體來說,也是一樣。
type?Person?struct?{
?name?string
?age?int
?gender?string
?address?string
}
var?p?=?Person{"Shenfq",?25,?"男",?"湖南長沙"}
var?v?=?reflect.ValueOf(p)
fmt.Println(v.Kind(),?v)

反射值對象還提供了一些方法,專門用來針對結(jié)構(gòu)體成員的信息獲取。
NumField()
NumField() 可以獲取結(jié)構(gòu)體成員的具體數(shù)量。
var?p?=?Person{"Shenfq",?25,?"男",?"湖南長沙"}
var?v?=?reflect.ValueOf(p)
fmt.Println("Person?結(jié)構(gòu)體成員數(shù):",?v.NumField())

Field()
Field() 可以獲取結(jié)構(gòu)體指定索引位置的成員的反射值。
var?p?=?Person{"Shenfq",?25,?"男",?"湖南長沙"}
var?v?=?reflect.ValueOf(p)
var?num?=?v.NumField()
for?i?:=0;?i?<?num;?i++?{
??var?val?=?v.Field(i)
??fmt.Printf("Person[%d]:?%s?%v\n",?i,?val.Type(),?val)
}

FieldByName()
FieldByName() 可以獲取結(jié)構(gòu)體指定成員名稱的成員的反射值。
var?p?=?Person{"Shenfq",?25,?"男",?"湖南長沙"}
var?v?=?reflect.ValueOf(p)
var?vOfName?=?v.FieldByName("name")
fmt.Printf("Person[name]:?%s?%v\n",?vOfName.Type(),?vOfName)
- END -