由於沒有強類型語言的經驗,反射這個概念,以前確實沒怎麼接觸過。在維基百科上搜了一下,具體解釋以下:編程
在計算機學中,反射式編程(英語:reflective programming)或反射(英語:reflection),是指計算機程序在運行時(runtime)能夠訪問、檢測和修改它自己狀態或行爲的一種能力。用比喻來講,反射就是程序在運行的時候可以「觀察」而且修改本身的行爲。markdown
go 中的反射也是這種做用,能夠在程序運行期間,獲取變量的類型與值的信息,而後進行訪問或或者修改。go 語言中,內置了 reflect
包,用來獲取一個變量的類型(type
)與值(value
),對應的方法分別爲 reflect.TypeOf()
和 reflect.ValueOf()
。函數
TypeOf
方法,會返回該變量的類型對象,類型對象下能夠獲取到變量的類型與種類。spa
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
關鍵字定義的類型,而種類是變量最終歸屬的類型。提及來可能比較蒼白,咱們直接上代碼。code
type num int
// 定義一個num類型的變量
var i num = 1
var typeOfNum = reflect.TypeOf(i)
複製代碼
能夠看到,此時的類型爲 num
,種類爲 int
。orm
對於一些引用類型的變量,好比切片、函數、結構體,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))
}
複製代碼
而面對匿名結構體或者匿名函數,其類型值會返回爲空。索引
func main() {
var a = struct {}{}
printTypeOf(reflect.TypeOf(a))
}
複製代碼
ValueOf
方法,能夠獲取一個變量的值。string
var i = 3.1415926
var s = "歡迎關注個人公衆號:『天然醒的筆記本』"
fmt.Println(reflect.ValueOf(s))
fmt.Println(reflect.ValueOf(i))
複製代碼
經過反射的值對象,也能取到變量的種類,而且還能根據其種類,調用對應的方法獲取變量的真實值。
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)
複製代碼
取出指針的值對象以後,不能當即賦值,由於此時拿到的是變量的地址。
要賦值的話,須要先調用 Elem
方法,取出具體元素,而後進行賦值。
var i = 100
var v = reflect.ValueOf(&i) // 取出變量i的指針的值對象
var e = v.Elem()
e.SetInt(500) // 修改元素值
fmt.Println(e.Kind(), i)
複製代碼
前面介紹過,經過反射能夠獲得變量的值,對於結構體來講,也是同樣。
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)
複製代碼
反射值對象還提供了一些方法,專門用來針對結構體成員的信息獲取。
NumField()
能夠獲取結構體成員的具體數量。
var p = Person{"Shenfq", 25, "男", "湖南長沙"}
var v = reflect.ValueOf(p)
fmt.Println("Person 結構體成員數:", v.NumField())
複製代碼
Field()
能夠獲取結構體指定索引位置的成員的反射值。
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()
能夠獲取結構體指定成員名稱的成員的反射值。
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)
複製代碼