這篇文章給大家分享的是有關go語言反射實現(xiàn)原理是什么的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
目前累計服務客戶上千余家,積累了豐富的產(chǎn)品開發(fā)及服務經(jīng)驗。以網(wǎng)站設計水平和技術實力,樹立企業(yè)形象,為客戶提供網(wǎng)站建設、做網(wǎng)站、網(wǎng)站策劃、網(wǎng)頁設計、網(wǎng)絡營銷、VI設計、網(wǎng)站改版、漏洞修補等服務。創(chuàng)新互聯(lián)始終以務實、誠信為根本,不斷創(chuàng)新和提高建站品質(zhì),通過對領先技術的掌握、對創(chuàng)意設計的研究、對客戶形象的視覺傳遞、對應用系統(tǒng)的結(jié)合,為客戶提供更好的一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進步。
Go反射的實現(xiàn)和 interface
和 unsafe.Pointer
密切相關。如果對golang的 interface
底層實現(xiàn)還沒有理解,可以去看我之前的文章: Go語言interface底層實現(xiàn) , unsafe.Pointer
會在后續(xù)的文章中做介紹。
(本文目前使用的Go環(huán)境是Go 1.12.9)
interface回顧
首先我們簡單的回顧一下interface的結(jié)構(gòu),總體上是:
細分下來分為有函數(shù)的 iface
和無函數(shù)的 eface
(就是 interface{}
);
無函數(shù)的 eface
有函數(shù)的 iface
靜態(tài)類型(static interface type)和動態(tài)混合類型(dynamic concrete type)
Go語言中,每個變量都有唯一個 靜態(tài)類型,這個類型是編譯階段就可以確定的。有的變量可能除了靜態(tài)類型之外,還會有 動態(tài)混合類型。
例如以下例子:
//帶函數(shù)的interface var r io.Reader tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty //不帶函數(shù)的interface var empty interface{} empty = tty
有函數(shù)的 iface
的例子
我們一句一句來看:第1行, var r io.Reader
第4行至第七行就是簡單的賦值,得到一個 *os.File
的實例,暫且不看了。最后一句第十句 r = tty
無函數(shù)的 eface
的例子
我們接著往下看, var empty interface{}
最后是 empty = tty
但是記?。弘m然有 動態(tài)混合類型,但是對外”表現(xiàn)”依然是靜態(tài)類型。
Go反射簡介
Go反射有三大法則:
//接口數(shù)據(jù) =====》 反射對象
1. Reflection goes from interface value to reflection object.
//反射對象 ===> 接口數(shù)據(jù)
2. Reflection goes from reflection object to interface value.
// 倘若數(shù)據(jù)可更改,可通過反射對象來修改它
3. To modify a reflection object, the value must be settable.
Go 的反射就是對以上三項法則的實現(xiàn)。
Go的反射主要由兩部分組成: Type
和 Value
, Type
和 Value
是倆結(jié)構(gòu)體:(這倆結(jié)構(gòu)體具體內(nèi)容可以略過不看,知道有這回事兒就行了)
Type:
type Type interface { Align() int FieldAlign() int Method(int) Method MethodByName(string) (Method, bool) NumMethod() int Name() string PkgPath() string Size() uintptr String() string Kind() Kind Implements(u Type) bool AssignableTo(u Type) bool ConvertibleTo(u Type) bool Comparable() bool Bits() int ChanDir() ChanDir IsVariadic() bool Elem() Type Field(i int) StructField FieldByIndex(index []int) StructField FieldByName(name string) (StructField, bool) FieldByNameFunc(match func(string) bool) (StructField, bool) In(i int) Type Key() Type Len() int NumField() int NumIn() int NumOut() int Out(i int) Type common() *rtype uncommon() *uncommonType }
Value:
type Value struct { typ *rtype ptr unsafe.Pointer flag }
你會發(fā)現(xiàn)反射的實現(xiàn)和interface的組成很相似,都是由“類型”和“數(shù)據(jù)值”構(gòu)成,但是值得注意的是:interface的“類型”和“數(shù)據(jù)值”是在“一起的”,而反射的“類型”和“數(shù)據(jù)值”是分開的。
Type
和 Value
提供了非常多的方法:例如獲取對象的屬性列表、獲取和修改某個屬性的值、對象所屬結(jié)構(gòu)體的名字、對象的底層類型(underlying type)等等
Go中的反射,在使用中最核心的就兩個函數(shù):
reflect.TypeOf(x)
reflect.ValueOf(x)
這兩個函數(shù)可以分別將給定的數(shù)據(jù)對象轉(zhuǎn)化為以上的 Type
和 Value
。這兩個都叫做 反射對象
Reflection goes from interface value to reflection object(法則一)
給定一個數(shù)據(jù)對象,可以將數(shù)據(jù)對象轉(zhuǎn)化為反射對象 Type
和 Value
。
事例代碼:
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 t := reflect.TypeOf(x) v := reflect.ValueOf(x) fmt.Println("type:", t) //type: float64 fmt.Println("value:", v.String()) //value:fmt.Println("type:", v.Type()) // type: float64 fmt.Println("kind is float64:", v.Kind() == reflect.Float64) //kind is float64: true fmt.Println("value:", v.Float()) //value: 3.4 }
由代碼17行可以看出: Value
還可以獲取到當前數(shù)據(jù)值的 Type
。
所以,法則一的圖應為:
Reflection goes from reflection object to interface value.(法則二)
給定的反射對象,可以轉(zhuǎn)化為某種類型的數(shù)據(jù)對象。即法則一的逆向。
注意 Type
是沒法逆向轉(zhuǎn)換的,仔細想想也合理,如果可逆類型轉(zhuǎn)化成什么呢?(#^.^#)
承接法則一的代碼:
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 t := reflect.TypeOf(x) v := reflect.ValueOf(x) ... o := v.Interface().(float64) // 法則2代碼 fmt.Println(o) }
To modify a reflection object, the value must be settable.(法則三)
法則三是說:通過反射對象,可以修改原數(shù)據(jù)中的內(nèi)容。
這里說的反射對象,是指 Value
,畢竟 Type
只是表示原數(shù)據(jù)的類型相關的內(nèi)容,而 Value
是對應著原數(shù)據(jù)對象本身。
在目前以上的所有例子中,反射得到的 Value
對象的修改,都是無法直接修改原數(shù)據(jù)對象的。
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 t := reflect.TypeOf(x) v := reflect.ValueOf(&x) .... o := v.Interface().(float64) fmt.Println(o) v.SetFloat(5.4) //此行會報錯 fmt.Println(x) }
這段代碼20行會報一個panic
reflect: reflect.Value.SetFloat using unaddressable value
這句話的意思并不是地址不可達,而是:對象 v
不可設置( settable
)。
我們可以通過 Value
結(jié)構(gòu)體的 CanSet()
方法來查看是否可以設置修改新值。
通過以下代碼可以知道 CanSet()
返回值是false。
fmt.Println(v.CanSet()) // false
如何通過反射對象來修改原數(shù)據(jù)對象的值呢?
如何才能可以通過反射對象來修改原數(shù)據(jù)對象的值或者說為什么不能設置呢?
原因簡單且純粹:在Go中,任何函數(shù)的參數(shù)都是值的拷貝,而非原數(shù)據(jù)。
反射函數(shù) reflect.ValueOf()
也不例外。我們目前得到的反射對象,都是原對象的copy的反射對象,而非原對象本身,所以不可以修改到原對象;即使可以修改,修改一個傳參時候的副本,也毫無意義,不如報錯兒。Go反射第三法則中的制定的 settable
屬性就由此而來,還延伸出了類似于 CanSet()
的方法。
那如何修改呢?
首先,在Go中要想讓函數(shù)“有副作用“,傳值必須傳指針類型的。
... var x float64 = 3.4 v := reflect.ValueOf(&x) ...
此時還不行,因為這樣反射對象對應的是原數(shù)據(jù)對象的指針類型,必須要拿到當前類型的值類型(*v),如何做?
Go提供了另外一個方法 Elem()
... var x float64 = 3.4 v := reflect.ValueOf(&x) p := v.Elem() fmt.Println(p.CanSet()) // true p.SetFloat(7.1) fmt.Println(x) // 7.1
看以上代碼,就可以修改原數(shù)據(jù)了。
反射原理
不難發(fā)現(xiàn),go的反射和interface在結(jié)構(gòu)上是如此的相近!都分為兩部分:一部分是 Type
一部分是 value
。
反射會不會是比著interface來實現(xiàn)的?
反射是什么意思?反射的意思是在運行時,能夠動態(tài)知道給定數(shù)據(jù)對象的類型和結(jié)構(gòu),并有機會修改它!
現(xiàn)在一個數(shù)據(jù)對象,如何判斷它是什么結(jié)構(gòu)?
數(shù)據(jù)interface中保存有結(jié)構(gòu)數(shù)據(jù)呀,只要想辦法拿到該數(shù)據(jù)對應的內(nèi)存地址,然后把該數(shù)據(jù)轉(zhuǎn)成interface,通過查看interface中的類型結(jié)構(gòu),就可以知道該數(shù)據(jù)的結(jié)構(gòu)了呀~
其實以上就是Go反射通俗的原理。
圖可以展示為:
圖中結(jié)構(gòu)中牽扯到的指針,都是 unsafe.Pointer
指針,具體這是個什么指針,后續(xù)的文章中會有介紹,在此,你就姑且認為是可以指向Go系統(tǒng)中任意數(shù)據(jù)的指針就可以。
源碼部分 (以下部分可以忽略,是我在查閱代碼時候遇到的一點點坑。)
我們來看看具體的源碼:源碼在”GO SDK/src/refelct“包中,具體主要是包中的”type.go”和”value.go”這兩個文件。
可以簡單的認為,反射的核心代碼,主要是 reflect.ValueOf()
和 reflect.TypeOf()
這兩個函數(shù)。
先看類型轉(zhuǎn)換: reflect.TypeOf()
// TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) }
其中出現(xiàn)了兩種數(shù)據(jù)結(jié)構(gòu),一個是 Type
,一個是 emptyInterface
分別看看這兩者的代碼:
emptyInterface
在 ”GO SDK/src/reflect/value.go“文件中
// emptyInterface is the header for an interface{} value. type emptyInterface struct { typ *rtype word unsafe.Pointer } // nonEmptyInterface is the header for an interface value with methods. type nonEmptyInterface struct { // see ../runtime/iface.go:/Itab itab *struct { ityp *rtype // static interface type typ *rtype // dynamic concrete type hash uint32 // copy of typ.hash _ [4]byte fun [100000]unsafe.Pointer // method table } word unsafe.Pointer }
go是golang的簡稱,golang 是Google開發(fā)的一種靜態(tài)強類型、編譯型、并發(fā)型,并具有垃圾回收功能的編程語言,其語法與 C語言相近,但并不包括如枚舉、異常處理、繼承、泛型、斷言、虛函數(shù)等功能。
感謝各位的閱讀!關于“go語言反射實現(xiàn)原理是什么”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!