下面由golang教程欄目給大家介紹詳解Golang的反射(實(shí)例),希望對(duì)需要的朋友有所幫助!
前言
反射在很多語(yǔ)言中都有其妙用。在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類(lèi)應(yīng)用,它們能夠***自描述***和***自控制***。
本文將記錄筆者對(duì)于Golang的反射的筆記。
10s后,以下知識(shí)點(diǎn)即將靠近:
1.反射的簡(jiǎn)介
2.為什么使用反射?
3.反射具體能做什么
正文
1.反射的簡(jiǎn)介
Golang提供了一種機(jī)制,在編譯時(shí)不知道類(lèi)型的情況下,可更新變量、運(yùn)行時(shí)查看值、調(diào)用方法以及直接對(duì)他們的布局進(jìn)行操作的機(jī)制,稱(chēng)為反射。
2.為什么使用反射?
打個(gè)比方,有時(shí)候我們需要一個(gè)函數(shù)可以處理各種類(lèi)型的值。在不知道類(lèi)型的情況下,你可能會(huì)這么寫(xiě):
// 偽代碼 switch value := value.(type) { case string: // ...一些操作 case int: // ...一些操作 case cbsStruct: // 自定義的結(jié)構(gòu)體 // ...一些操作 // ... }
有沒(méi)發(fā)現(xiàn)什么問(wèn)題?
這邊存在一個(gè)問(wèn)題:類(lèi)型很多,這個(gè)函數(shù)會(huì)寫(xiě)的非常長(zhǎng),而且還可能存在自定的類(lèi)型,也就是說(shuō)這個(gè)判斷日后可能還要一直改,因?yàn)闊o(wú)法知道未知值到底屬于什么類(lèi)型。
無(wú)法透視一個(gè)未知類(lèi)型的時(shí)候,以上代碼其實(shí)不是很合理,這時(shí)候就需要有反射來(lái)幫忙你處理,反射使用TypeOf和ValueOf函數(shù)從接口中獲取目標(biāo)對(duì)象的信息,輕松完成目的。
3.反射具體能做什么?
1.獲取變量?jī)?nèi)部信息
reflect提供了兩種類(lèi)型來(lái)進(jìn)行訪(fǎng)問(wèn)接口變量的內(nèi)容:
類(lèi)型 | 作用 |
---|---|
reflect.ValueOf() | 獲取輸入?yún)?shù)接口中的數(shù)據(jù)的值,如果為空則返回0 <- 注意是0 |
reflect.TypeOf() | 動(dòng)態(tài)獲取輸入?yún)?shù)接口中的值的類(lèi)型,如果為空則返回nil <- 注意是nil |
上代碼
package main import ( "fmt" "reflect" ) func main() { var name string = "咖啡色的羊駝" // TypeOf會(huì)返回目標(biāo)數(shù)據(jù)的類(lèi)型,比如int/float/struct/指針等 reflectType := reflect.TypeOf(name) // valueOf返回目標(biāo)數(shù)據(jù)的的值,比如上文的"咖啡色的羊駝" reflectValue := reflect.ValueOf(name) fmt.Println("type: ", reflectType) fmt.Println("value: ", reflectValue) }
輸出:
type: string value: 咖啡色的羊駝
更深一層:在以上操作發(fā)生的時(shí)候,反射將“接口類(lèi)型的變量”轉(zhuǎn)為了“反射的接口類(lèi)型的變量”,比如上文實(shí)際上返回的是reflect.Value和reflect.Type的接口對(duì)象。(可以根據(jù)ide跟蹤一下相關(guān)函數(shù)返回類(lèi)型便知)
2.struct的反射
package main import ( "fmt" "reflect" ) type Student struct { Id int Name string } func (s Student) Hello(){ fmt.Println("我是一個(gè)學(xué)生") } func main() { s := Student{Id: 1, Name: "咖啡色的羊駝"} // 獲取目標(biāo)對(duì)象 t := reflect.TypeOf(s) // .Name()可以獲取去這個(gè)類(lèi)型的名稱(chēng) fmt.Println("這個(gè)類(lèi)型的名稱(chēng)是:", t.Name()) // 獲取目標(biāo)對(duì)象的值類(lèi)型 v := reflect.ValueOf(s) // .NumField()來(lái)獲取其包含的字段的總數(shù) for i := 0; i < t.NumField(); i++ { // 從0開(kāi)始獲取Student所包含的key key := t.Field(i) // 通過(guò)interface方法來(lái)獲取key所對(duì)應(yīng)的值 value := v.Field(i).Interface() fmt.Printf("第%d個(gè)字段是:%s:%v = %v n", i+1, key.Name, key.Type, value) } // 通過(guò).NumMethod()來(lái)獲取Student里頭的方法 for i:=0;i<t.NumMethod(); i++ { m := t.Method(i) fmt.Printf("第%d個(gè)方法是:%s:%vn", i+1, m.Name, m.Type) } }
輸出:
這個(gè)類(lèi)型的名稱(chēng)是: Student 第1個(gè)字段是:Id:int = 1 第2個(gè)字段是:Name:string = 咖啡色的羊駝 第1個(gè)方法是:Hello:func(main.Student)
3.匿名或嵌入字段的反射
package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } type People struct { Student // 匿名字段 } func main() { p := People{Student{Id: 1, Name: "咖啡色的羊駝"}} t := reflect.TypeOf(p) // 這里需要加一個(gè)#號(hào),可以把struct的詳情都給打印出來(lái) // 會(huì)發(fā)現(xiàn)有Anonymous:true,說(shuō)明是匿名字段 fmt.Printf("%#vn", t.Field(0)) // 取出這個(gè)學(xué)生的名字的詳情打印出來(lái) fmt.Printf("%#vn", t.FieldByIndex([]int{0, 1})) // 獲取匿名字段的值的詳情 v := reflect.ValueOf(p) fmt.Printf("%#vn", v.Field(0)) }
輸出:
reflect.StructField{Name:"Student", PkgPath:"", Type:(*reflect.rtype)(0x10aade0), Tag:"", Offset:0x0, Index:[]int{0}, Anonymous:true} reflect.StructField{Name:"Name", PkgPath:"", Type:(*reflect.rtype)(0x109f4e0), Tag:"", Offset:0x8, Index:[]int{1}, Anonymous:false} main.Student{Id:1, Name:"咖啡色的羊駝"}
4.判斷傳入的類(lèi)型是否是我們想要的類(lèi)型
package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } func main() { s := Student{Id: 1, Name: "咖啡色的羊駝"} t := reflect.TypeOf(s) // 通過(guò).Kind()來(lái)判斷對(duì)比的值是否是struct類(lèi)型 if k := t.Kind(); k == reflect.Struct { fmt.Println("bingo") } num := 1; numType := reflect.TypeOf(num) if k := numType.Kind(); k == reflect.Int { fmt.Println("bingo") } }
輸出:
bingo bingo
5.通過(guò)反射修改內(nèi)容
package main import ( "reflect" "fmt" ) type Student struct { Id int Name string } func main() { s := &Student{Id: 1, Name: "咖啡色的羊駝"} v := reflect.ValueOf(s) // 修改值必須是指針類(lèi)型否則不可行 if v.Kind() != reflect.Ptr { fmt.Println("不是指針類(lèi)型,沒(méi)法進(jìn)行修改操作") return } // 獲取指針?biāo)赶虻脑? v = v.Elem() // 獲取目標(biāo)key的Value的封裝 name := v.FieldByName("Name") if name.Kind() == reflect.String { name.SetString("小學(xué)生") } fmt.Printf("%#v n", *s) // 如果是整型的話(huà) test := 888 testV := reflect.ValueOf(&test) testV.Elem().SetInt(666) fmt.Println(test) }
輸出:
main.Student{Id:1, Name:"小學(xué)生"} 666
6.通過(guò)反射調(diào)用方法
package main import ( "fmt" "reflect" ) type Student struct { Id int Name string } func (s Student) EchoName(name string){ fmt.Println("我的名字是:", name) } func main() { s := Student{Id: 1, Name: "咖啡色的羊駝"} v := reflect.ValueOf(s) // 獲取方法控制權(quán) // 官方解釋?zhuān)悍祷豽的名為name的方法的已綁定(到v的持有值的)狀態(tài)的函數(shù)形式的Value封裝 mv := v.MethodByName("EchoName") // 拼湊參數(shù) args := []reflect.Value{reflect.ValueOf("咖啡色的羊駝")} // 調(diào)用函數(shù) mv.Call(args) }
輸出:
我的名字是: 咖啡色的羊駝
##4.反射的一些小點(diǎn)
1.使用反射時(shí)需要先確定要操作的值是否是期望的類(lèi)型,是否是可以進(jìn)行“賦值”操作的,否則reflect包將會(huì)毫不留情的產(chǎn)生一個(gè)panic。
2.反射主要與Golang的interface類(lèi)型相關(guān),只有interface類(lèi)型才有反射一說(shuō)。如果有興趣可以看一下TypeOf和ValueOf,會(huì)發(fā)現(xiàn)其實(shí)傳入?yún)?shù)的時(shí)候已經(jīng)被轉(zhuǎn)為接口類(lèi)型了。
// 以下為截取的源代碼 func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } func ValueOf(i interface{}) Value { if i == nil { return Value{} } escapes(i) return unpackEface(i) }