Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析
1. 反射的引入與介紹
在計算機科學(xué)中,反射是指計算機程序在運行時(Run time)可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。用比喻來說,反射就是程序在運行的時候能夠“觀察”并且修改自己的行為。
需要反射的 2 個常見場景:
- 有時你需要編寫一個函數(shù),但是并不知道傳給你的參數(shù)類型是什么,可能是沒約定好;也可能是傳入的類型很多,這些類型并不能統(tǒng)一表示。這時反射就會用的上了。
- 有時候需要根據(jù)某些條件決定調(diào)用哪個函數(shù),比如根據(jù)用戶的輸入來決定。這時就需要對函數(shù)和函數(shù)的參數(shù)進行反射,在運行期間動態(tài)地執(zhí)行函數(shù)。
Java中動態(tài)代理與AOP的實現(xiàn),就要借助這種操作。而對于我們Go語言來說,也提供了反射的機制。
有了前面對于interface{}底層數(shù)據(jù)結(jié)構(gòu)的了解,Go中的每個實例對象可以分為兩快,類型信息與數(shù)值信息。而我們Go中提供的反射機制,能分別拿到這兩塊信息。
數(shù)據(jù)interface中保存有結(jié)構(gòu)數(shù)據(jù),拿到該數(shù)據(jù)對應(yīng)的內(nèi)存地址,然后把該數(shù)據(jù)轉(zhuǎn)成interface,通過查看interface中的類型結(jié)構(gòu),就可以知道該數(shù)據(jù)的結(jié)構(gòu)了,其實以上就是Go反射通俗的原理。
2. 反射的數(shù)據(jù)結(jié)構(gòu)
type Type interface { // 所有的類型都可以調(diào)用下面這些函數(shù) // 此類型的變量對齊后所占用的字節(jié)數(shù) Align() int // 如果是 struct 的字段,對齊后占用的字節(jié)數(shù) FieldAlign() int // 返回類型方法集里的第 `i` (傳入的參數(shù))個方法 Method(int) Method // 通過名稱獲取方法 MethodByName(string) (Method, bool) // 獲取類型方法集里導(dǎo)出的方法個數(shù) NumMethod() int // 類型名稱 Name() string // 返回類型所在的路徑,如:encoding/base64 PkgPath() string // 返回類型的大小,和 unsafe.Sizeof 功能類似 Size() uintptr // 返回類型的字符串表示形式 String() string // 返回類型的類型值 Kind() Kind // 類型是否實現(xiàn)了接口 u Implements(u Type) bool // 是否可以賦值給 u AssignableTo(u Type) bool // 是否可以類型轉(zhuǎn)換成 u ConvertibleTo(u Type) bool // 類型是否可以比較 Comparable() bool // 下面這些函數(shù)只有特定類型可以調(diào)用 // 如:Key, Elem 兩個方法就只能是 Map 類型才能調(diào)用 // 類型所占據(jù)的位數(shù) Bits() int // 返回通道的方向,只能是 chan 類型調(diào)用 ChanDir() ChanDir // 返回類型是否是可變參數(shù),只能是 func 類型調(diào)用 // 比如 t 是類型 func(x int, y ... float64) // 那么 t.IsVariadic() == true IsVariadic() bool // 返回內(nèi)部子元素類型,只能由類型 Array, Chan, Map, Ptr, or Slice 調(diào)用 Elem() Type // 返回結(jié)構(gòu)體類型的第 i 個字段,只能是結(jié)構(gòu)體類型調(diào)用 // 如果 i 超過了總字段數(shù),就會 panic Field(i int) StructField // 返回嵌套的結(jié)構(gòu)體的字段 FieldByIndex(index []int) StructField // 通過字段名稱獲取字段 FieldByName(name string) (StructField, bool) // FieldByNameFunc returns the struct field with a name // 返回名稱符合 func 函數(shù)的字段 FieldByNameFunc(match func(string) bool) (StructField, bool) // 獲取函數(shù)類型的第 i 個參數(shù)的類型 In(i int) Type // 返回 map 的 key 類型,只能由類型 map 調(diào)用 Key() Type // 返回 Array 的長度,只能由類型 Array 調(diào)用 Len() int // 返回類型字段的數(shù)量,只能由類型 Struct 調(diào)用 NumField() int // 返回函數(shù)類型的輸入?yún)?shù)個數(shù) NumIn() int // 返回函數(shù)類型的返回值個數(shù) NumOut() int // 返回函數(shù)類型的第 i 個值的類型 Out(i int) Type // 返回類型結(jié)構(gòu)體的相同部分 common() *rtype // 返回類型結(jié)構(gòu)體的不同部分 uncommon() *uncommonType } type Value struct { typ *rtype ptr unsafe.Pointer flag }
反射的實現(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
。這兩個都叫做反射對象
3. 如何通過反射對象來修改原數(shù)據(jù)對象的值?
在Go中,任何函數(shù)的參數(shù)都是值的拷貝,而非原數(shù)據(jù)。
反射函數(shù)reflect.ValueOf()
也不例外。我們目前得到的反射對象,都是原對象的copy的反射對象,而非原對象本身,所以不可以修改到原對象.
那如何修改呢?
首先,在Go中要想讓函數(shù)“有副作用“,傳值必須傳指針類型的。
var x float64 = 5.7 v := reflect.ValueOf(&x)
時還不行,因為這樣反射對象對應(yīng)的是原數(shù)據(jù)對象的指針類型,必須要拿到當前類型的值類型(*v)。 Go提供了另外一個方法Elem()
p := v.Elem() fmt.Println(p.CanSet()) // true p.SetFloat(6.6) fmt.Println(x) // 6.6
經(jīng)過以上操作,就可以修改原數(shù)據(jù)了,完整過程如下:
var x float64 = 5.7 v := reflect.ValueOf(&x) p := v.Elem() fmt.Println(p.CanSet()) // true p.SetFloat(6.6) fmt.Println(x) // 6.6
到此這篇關(guān)于Go反射底層原理及數(shù)據(jù)結(jié)構(gòu)解析的文章就介紹到這了,更多相關(guān) Go反射原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java IO流 之 輸入流 InputString()的使用
這篇文章主要介紹了java IO流 之 輸入流 InputString()的使用,以及讀取數(shù)據(jù)的三種方式詳解,非常不錯,需要的朋友可以參考下2016-12-12解析Java和Eclipse中加載本地庫(.dll文件)的詳細說明
本篇文章是對Java和Eclipse中加載本地庫(.dll文件)進行了詳細的分析介紹,需要的朋友參考下2013-05-05Dubbo?retries?超時重試機制的問題原因分析及解決方案
這篇文章主要介紹了Dubbo?retries?超時重試機制的問題,解決方案是通過修改dubbo服務(wù)提供方,將timeout超時設(shè)為20000ms或者設(shè)置retries=“0”,禁用超時重試機制,感興趣的朋友跟隨小編一起看看吧2022-04-04Springboot AOP對指定敏感字段數(shù)據(jù)加密存儲的實現(xiàn)
本篇文章主要介紹了利用Springboot+AOP對指定的敏感數(shù)據(jù)進行加密存儲以及對數(shù)據(jù)中加密的數(shù)據(jù)的解密的方法,代碼詳細,具有一定的價值,感興趣的小伙伴可以了解一下2021-11-11Java中如何利用Set判斷List集合中是否有重復(fù)元素
在開發(fā)工作中,我們有時需要去判斷List集合中是否含有重復(fù)的元素,這時候我們不需要找出重復(fù)的元素,我們只需要返回一個?Boolean?類型就可以了,下面通過本文給大家介紹Java中利用Set判斷List集合中是否有重復(fù)元素,需要的朋友可以參考下2023-05-05