文章目錄
- 一 反射簡介
- 二 反射是如何實作的
- 三 Go中反射初識
- 3.1 reflect包的兩個函式
- 3.2 靜態型別與動態型別
一 反射簡介
反射是指在程式運行期對程式本身進行訪問和修改的能力,即可以在運行時動態獲取變數的各種資訊,比如變數的型別(type),類別(kind),如果是結構體變數,還可以獲取到結構體本身的資訊(欄位與方法),通過反射,還可以修改變數的值,可以呼叫關聯的方法,
反射常用在框架的開發上,一些常見的案例,如JSON序列化時候tag標簽的產生,配接器函式的制作等,都需要用到反射,反射的兩個使用常見使用場景:
- 不知道函式的引數型別:沒有約定好引數、傳入型別很多,此時型別不能統一表示,需要反射
- 不知道呼叫哪個函式:比如根據用戶的輸入來決定呼叫特定函式,此時需要依據函式、函式引數進行反射,在運行期間動態執行函式
Go程式的反射系統無法獲取到一個可執行檔案空間中或者是一個包中的所有型別資訊,需要配合使用標準庫中對應的詞法、語法決議器和抽象語法樹( AST) 對原始碼進行掃描后獲得這些資訊,
貼士:
- C,C++沒有支持反射功能,只能通過 typeid 提供非常榷訓的程式運行時型別資訊,
- Java、 C#等語言都支持完整的反射功能,
- Lua、JavaScript類動態語言,由于其本身的語法特性就可以讓代碼在運行期訪問程式自身的值和型別資訊,因此不需要反射系統,
注意:
- 在編譯期間,無法對反射代碼進行一些錯誤提示,
- 反射影響性能
二 反射是如何實作的
反射是通過介面的型別資訊實作的,即反射建立在型別的基礎上:當向介面變數賦予一個物體型別的時候,介面會存盤物體的型別資訊,
Go中反射相關的包是reflect,在該包中,定義了各種型別,實作了反射的各種函式,通過它們可以在運行時檢測型別的資訊、改變型別的值,
變數包括type、value兩個部分(所以 nil != nil ),type包括兩部分:
- static type:在開發時使用的型別,如int、string
- concrete type:是runtime系統使用的型別
型別能夠斷言成功,取決于 concrete type ,如果一個reader變數,如果 concrete type 實作了 write 方法,那么它可以被型別斷言為writer,
Go中,反射與interface型別相關,其type是 concrete type,只有interface才有反射!每個interface變數都有一個對應的pair,pair中記錄了變數的實際值和型別(value, type),即一個介面型別變數包含2個指標,一個指向對應的 concrete type ,另一個指向實際的值 value,
示例:
var r io.Reader // 定義了一個介面型別
r, err := os.OpenFile() // 記錄介面的實際型別、實際值
var w io.Writer // 定義一個介面型別
w = r.(io.Writer) // 賦值時,介面內部的pair不變,所以 w 和 r 是同一型別
三 Go中反射初識
3.1 reflect包的兩個函式
reflect 提供了2個重要函式:
- ValueOf():獲取變數的值,即pair中的 value
- TypeOf():獲取變數的型別,即pair中的 concrete type
type Person struct {
Name string
Age int
}
p := Person{ "lisi", 13}
fmt.Println(reflect.ValueOf(p)) // {lisi 13} 變數的值
fmt.Println(reflect.ValueOf(p).Type()) // main.Person 變數型別的物件名
fmt.Println(reflect.TypeOf(p)) // main.Person 變數型別的物件名
fmt.Println(reflect.TypeOf(p).Name()) // Person:變數型別物件的型別名
fmt.Println(reflect.TypeOf(p).Kind()) // struct:變數型別物件的種類名
fmt.Println(reflect.TypeOf(p).Name() == "Person") // true
fmt.Println(reflect.TypeOf(p).Kind() == reflect.Struct) //true
型別與種類的區別:
- Type是原生資料型別: int、string、bool、float32 ,以及 type 定義的型別,對應的反射獲取方法是 reflect.Type 中 的 Name()
- Kind是物件歸屬的品種:Int、Bool、Float32、Chan、String、Struct、Ptr(指標)、Map、Interface、Fune、Array、Slice、Unsafe Pointer等
3.2 靜態型別與動態型別
靜態型別:變數宣告時候賦予的型別
type MyInt int // int 是靜態型別
var i *int // *int 是靜態型別
動態型別:運行時給這個變數賦值時,這個值的型別即為動態型別(為nil時沒有動態型別),
var A interface{} // 空介面 是靜態型別,必須是介面型別才能實作型別動態變化
A = 10 // 此時靜態型別為 interface{} 動態為int
A = "hello" // 此時靜態型別為 interface{} 動態為string
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/225409.html
標籤:區塊鏈
