15-反射-1-概述
文章目錄
- 15-反射-1-概述
- 一 反射簡介
- 二 反射是如何實作的
- 三 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 是靜态類型
var A interface{} // 空接口 是靜态類型,必須是接口類型才能實作類型動态變化
A = 10 // 此時靜态類型為 interface{} 動态為int
A = "hello" // 此時靜态類型為 interface{} 動态為string