go語言有反射。 Go語言提供了一種機制在執行時間更新和檢查變數的值、呼叫變數的方法和變數支援的內在操作,但是在編譯時並不知道這些變數的具體類型,這種機制稱為反射。 Go語言中的反射是由reflect包提供支援的,它定義了兩個重要的類型Type和Value任意介面值在反射中都可以理解為由reflect.Type和reflect.Value兩部分組成。
本教學操作環境: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語言有反射嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!