go語言有反射。Go語言提供了一種機制在執行時更新和檢查變數的值、呼叫變數的方法和變數支援的內在操作,但是在編譯時並不知道這些變數的具體型別,這種機制被稱為反射。Go語言中的反射是由reflect包提供支援的,它定義了兩個重要的型別Type和Value任意介面值在反射中都可以理解為由reflect.Type和reflect.Value兩部分組成。
php入門到就業線上直播課:進入學習
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API偵錯工具:
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
Go語言提供了一種機制在執行時更新和檢查變數的值、呼叫變數的方法和變數支援的內在操作,但是在編譯時並不知道這些變數的具體型別,這種機制被稱為反射。反射也可以讓我們將型別本身作為第一類的值型別處理。
go語言中的反射
反射是指在程式執行期對程式本身進行存取和修改的能力,程式在編譯時變數被轉換為記憶體地址,變數名不會被編譯器寫入到可執行部分,在執行程式時程式無法獲取自身的資訊。
支援反射的語言可以在程式編譯期將變數的反射資訊,如欄位名稱、型別資訊、結構體資訊等整合到可執行檔案中,並給程式提供介面存取反射資訊,這樣就可以在程式執行期獲取型別的反射資訊,並且有能力修改它們。
C/C++語言沒有支援反射功能,只能通過 typeid 提供非常弱化的程式執行時型別資訊;Java、C# 等語言都支援完整的反射功能;Lua、JavaScript 類動態語言,由於其本身的語法特性就可以讓程式碼在執行期存取程式自身的值和型別資訊,因此不需要反射系統。
Go語言程式的反射系統無法獲取到一個可執行檔案空間中或者是一個包中的所有型別資訊,需要配合使用標準庫中對應的詞法、語法解析器和抽象語法樹(AST)對原始碼進行掃描後獲得這些資訊。
Go語言提供了 reflect 包來存取程式的反射資訊。
reflect 包
Go語言中的反射是由 reflect 包提供支援的,它定義了兩個重要的型別 Type 和 Value 任意介面值在反射中都可以理解為由 reflect.Type 和 reflect.Value 兩部分組成,並且 reflect 包提供了 reflect.TypeOf 和 reflect.ValueOf 兩個函數來獲取任意物件的 Value 和 Type。
反射的型別物件(reflect.Type)
在Go語言程式中,使用 reflect.TypeOf() 函數可以獲得任意值的型別物件(reflect.Type),程式通過型別物件可以存取任意值的型別資訊,下面通過範例來理解獲取型別物件的過程:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int
typeOfA := reflect.TypeOf(a)
fmt.Println(typeOfA.Name(), typeOfA.Kind())
}
登入後複製
執行結果如下:
程式碼說明如下:
第 9 行,定義一個 int 型別的變數。
第 10 行,通過 reflect.TypeOf() 取得變數 a 的型別物件 typeOfA,型別為 reflect.Type()。
第 11 行中,通過 typeOfA 型別物件的成員函數,可以分別獲取到 typeOfA 變數的型別名為 int,種類(Kind)為 int。
反射的型別(Type)與種類(Kind)
在使用反射時,需要首先理解型別(Type)和種類(Kind)的區別。程式設計中,使用最多的是型別,但在反射中,當需要區分一個大品種的型別時,就會用到種類(Kind)。例如需要統一判斷型別中的指標時,使用種類(Kind)資訊就較為方便。
1) 反射種類(Kind)的定義
Go語言程式中的型別(Type)指的是系統原生資料型別,如 int、string、bool、float32 等型別,以及使用 type 關鍵字定義的型別,這些型別的名稱就是其型別本身的名稱。例如使用 type A struct{} 定義結構體時,A 就是 struct{} 的型別。
種類(Kind)指的是物件歸屬的品種,在 reflect 包中有如下定義:
type Kind uint
const (
Invalid Kind = iota // 非法型別
Bool // 布林型
Int // 有符號整型
Int8 // 有符號8位元整型
Int16 // 有符號16位元整型
Int32 // 有符號32位元整型
Int64 // 有符號64位元整型
Uint // 無符號整型
Uint8 // 無符號8位元整型
Uint16 // 無符號16位元整型
Uint32 // 無符號32位元整型
Uint64 // 無符號64位元整型
Uintptr // 指標
Float32 // 單精度浮點數
Float64 // 雙精度浮點數
Complex64 // 64位元複數型別
Complex128 // 128位元複數型別
Array // 陣列
Chan // 通道
Func // 函數
Interface // 介面
Map // 對映
Ptr // 指標
Slice // 切片
String // 字串
Struct // 結構體
UnsafePointer // 底層指標
)
登入後複製
Map、Slice、Chan 屬於參照型別,使用起來類似於指標,但是在種類常數定義中仍然屬於獨立的種類,不屬於 Ptr。type A struct{} 定義的結構體屬於 Struct 種類,*A 屬於 Ptr。
2) 從型別物件中獲取型別名稱和種類
Go語言中的型別名稱對應的反射獲取方法是 reflect.Type 中的 Name() 方法,返回表示型別名稱的字串;型別歸屬的種類(Kind)使用的是 reflect.Type 中的 Kind() 方法,返回 reflect.Kind 型別的常數。
下面的程式碼中會對常數和結構體進行型別資訊獲取。
package main
import (
"fmt"
"reflect"
)
// 定義一個Enum型別
type Enum int
const (
Zero Enum = 0
)
func main() {
// 宣告一個空結構體
type cat struct {
}
// 獲取結構體範例的反射型別物件
typeOfCat := reflect.TypeOf(cat{})
// 顯示反射型別物件的名稱和種類
fmt.Println(typeOfCat.Name(), typeOfCat.Kind())
// 獲取Zero常數的反射型別物件
typeOfA := reflect.TypeOf(Zero)
// 顯示反射型別物件的名稱和種類
fmt.Println(typeOfA.Name(), typeOfA.Kind())
}
登入後複製
執行結果如下:
程式碼說明如下:
第 17 行,宣告結構體型別 cat。
第 20 行,將 cat 範例化,並且使用 reflect.TypeOf() 獲取被範例化後的 cat 的反射型別物件。
第 22 行,輸出 cat 的型別名稱和種類,型別名稱就是 cat,而 cat 屬於一種結構體種類,因此種類為 struct。
第 24 行,Zero 是一個 Enum 型別的常數。這個 Enum 型別在第 9 行宣告,第 12 行宣告了常數。如沒有常數也不能建立範例,通過 reflect.TypeOf() 直接獲取反射型別物件。
第 26 行,輸出 Zero 對應的型別物件的型別名和種類。
指標與指標指向的元素
Go語言程式中對指標獲取反射物件時,可以通過 reflect.Elem() 方法獲取這個指標指向的元素型別,這個獲取過程被稱為取元素,等效於對指標型別變數做了一個*操作,程式碼如下:
package main
import (
"fmt"
"reflect"
)
func main() {
// 宣告一個空結構體
type cat struct {
}
// 建立cat的範例
ins := &cat{}
// 獲取結構體範例的反射型別物件
typeOfCat := reflect.TypeOf(ins)
// 顯示反射型別物件的名稱和種類
fmt.Printf("name:'%v' kind:'%v'\n", typeOfCat.Name(), typeOfCat.Kind())
// 取型別的元素
typeOfCat = typeOfCat.Elem()
// 顯示反射型別物件的名稱和種類
fmt.Printf("element name: '%v', element kind: '%v'\n", typeOfCat.Name(), typeOfCat.Kind())
}
登入後複製
執行結果如下:
程式碼說明如下:
第 13 行,建立了 cat 結構體的範例,ins 是一個 *cat 型別的指標變數。
第 15 行,對指標變數獲取反射型別資訊。
第 17 行,輸出指標變數的型別名稱和種類。Go語言的反射中對所有指標變數的種類都是 Ptr,但需要注意的是,指標變數的型別名稱是空,不是 *cat。
第 19 行,取指標型別的元素型別,也就是 cat 型別。這個操作不可逆,不可以通過一個非指標型別獲取它的指標型別。
第 21 行,輸出指標變數指向元素的型別名稱和種類,得到了 cat 的型別名稱(cat)和種類(struct)。
【相關推薦:Go視訊教學】
以上就是go語言有反射嗎的詳細內容,更多請關注TW511.COM其它相關文章!