GoLang反射機(jī)制深入講解
反射
Go語(yǔ)言提供了reflect 包來(lái)訪問程序的反射信息;定義了兩個(gè)重要的類型Type和Value:
- reflect.TypeOf:獲取任意值的類型對(duì)象(reflect.Type);
- reflect.ValueOf:獲得值的反射值對(duì)象(reflect.Value);
反射類型Type
Go語(yǔ)言程序中的類型(Type)指的是系統(tǒng)原生數(shù)據(jù)類型(如 int、string、bool、float32 等),以及使用 type 關(guān)鍵字定義的類型;而反射種類(Kind)是指對(duì)象的歸屬分類:
type Kind uint
const (
Invalid Kind = iota // 非法類型
Bool // 布爾型
Int // 有符號(hào)整型
Int8 // 有符號(hào)8位整型
Int16 // 有符號(hào)16位整型
Int32 // 有符號(hào)32位整型
Int64 // 有符號(hào)64位整型
Uint // 無(wú)符號(hào)整型
Uint8 // 無(wú)符號(hào)8位整型
Uint16 // 無(wú)符號(hào)16位整型
Uint32 // 無(wú)符號(hào)32位整型
Uint64 // 無(wú)符號(hào)64位整型
Uintptr // 指針
Float32 // 單精度浮點(diǎn)數(shù)
Float64 // 雙精度浮點(diǎn)數(shù)
Complex64 // 64位復(fù)數(shù)類型
Complex128 // 128位復(fù)數(shù)類型
Array // 數(shù)組
Chan // 通道
Func // 函數(shù)
Interface // 接口
Map // 映射
Ptr // 指針
Slice // 切片
String // 字符串
Struct // 結(jié)構(gòu)體
UnsafePointer // 底層指針
)
指針
對(duì)指針指向的對(duì)象,可通過(guò)reflect.Elem() 方法獲取這個(gè)指針指向的元素類型(等效于對(duì)指針類型變量做了一個(gè)*操作)。
func reflectStruct() {
type Cat struct {
}
aCat := &Cat{}
// 獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typeOfCat := reflect.TypeOf(aCat)
fmt.Printf("ptr 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())
typeOfCat = reflect.TypeOf(*aCat)
fmt.Printf("*ptr name:'%v' kind:'%v'\n", typeOfCat.Name(), typeOfCat.Kind())
}
// ptr name:'' kind:'ptr'
// element name: 'Cat', element kind: 'struct'
// *ptr name:'Cat' kind:'struct'
結(jié)構(gòu)體
對(duì)結(jié)構(gòu)體對(duì)象,獲取對(duì)象信息后,可通過(guò)NumField() 和 Field() 方法獲得結(jié)構(gòu)體成員的詳細(xì)信息。
| 方法 | 說(shuō)明 |
|---|---|
| Field(i int) StructField | 根據(jù)索引返回索引對(duì)應(yīng)字段的信息 |
| NumField() int | 返回結(jié)構(gòu)體成員字段數(shù)量 |
| FieldByName(name string) (StructField, bool) | 根據(jù)給定字符串返回字符串對(duì)應(yīng)的結(jié)構(gòu)體字段的信息,沒有找到時(shí) bool 返回 false |
| FieldByIndex(index []int) StructField | 多層成員訪問時(shí),根據(jù) []int 提供的每個(gè)結(jié)構(gòu)體的字段索引,返回字段的信息,沒有找到時(shí)返回零值 |
| FieldByNameFunc(match func(string) bool) (StructField,bool) | 根據(jù)匹配函數(shù)匹配需要的字段 |
字段信息中含有:
type StructField struct {
Name string // 字段名
PkgPath string // 字段在結(jié)構(gòu)體中的路徑
Type Type // 字段的反射類型 reflect.Type
Tag StructTag // 字段的結(jié)構(gòu)體標(biāo)簽
Offset uintptr // 字段在結(jié)構(gòu)體中的相對(duì)偏移
Index []int // FieldByIndex中的索引順序
Anonymous bool // 是否為匿名字段
}
獲取字段的名稱與tag:
func reflectStructField() {
// 聲明一個(gè)空結(jié)構(gòu)體
type Cat struct {
Name string
// 帶有結(jié)構(gòu)體tag的字段
Type int `json:"type" id:"100"`
}
aCat := Cat{Name: "mimi", Type: 1}
// 獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typeOfCat := reflect.TypeOf(aCat)
// 遍歷結(jié)構(gòu)體所有成員
for i := 0; i < typeOfCat.NumField(); i++ {
fieldType := typeOfCat.Field(i)
fmt.Printf("Field-%v: name: %v tag: '%v'\n", i, fieldType.Name, fieldType.Tag)
}
// 通過(guò)字段名, 找到字段類型信息
if catType, ok := typeOfCat.FieldByName("Type"); ok {
fmt.Println("Field Tag: ", catType.Tag.Get("json"), catType.Tag.Get("id"))
}
}
// Field-0: name: Name tag: ''
// Field-1: name: Type tag: 'json:"type" id:"100"'
// Field Tag: type 100
反射值Value
通過(guò)下面幾種方法從反射值對(duì)象 reflect.Value 中獲取原值
| 方法名 | 說(shuō) 明 |
|---|---|
| Interface() interface {} | 將值以 interface{} 類型返回,可以通過(guò)類型斷言轉(zhuǎn)換為指定類型 |
| Int() int64 | 將值以 int 類型返回,所有有符號(hào)整型均可以此方式返回 |
| Uint() uint64 | 將值以 uint 類型返回,所有無(wú)符號(hào)整型均可以此方式返回 |
| Float() float64 | 將值以雙精度(float64)類型返回,所有浮點(diǎn)數(shù)(float32、float64)均可以此方式返回 |
| Bool() bool | 將值以 bool 類型返回 |
| Bytes() []bytes | 將值以字節(jié)數(shù)組 []bytes 類型返回 |
| String() string | 將值以字符串類型返回 |
通過(guò)反射獲取變量的值:
func reflectValue() {
var a int = 1024
// 獲取變量a的反射值對(duì)象
valueOfA := reflect.ValueOf(a)
// 獲取interface{}類型的值, 通過(guò)類型斷言轉(zhuǎn)換
var getA int = valueOfA.Interface().(int)
// 獲取64位的值, 強(qiáng)制類型轉(zhuǎn)換為int類型
var getA2 int = int(valueOfA.Int())
fmt.Println(getA, getA2)
}
// 1024 1024
結(jié)構(gòu)體
反射值對(duì)象(reflect.Value)提供對(duì)結(jié)構(gòu)體訪問的方法,通過(guò)這些方法可以完成對(duì)結(jié)構(gòu)體任意值的訪問:
| 方 法 | 備 注 |
|---|---|
| Field(i int) Value | 根據(jù)索引,返回索引對(duì)應(yīng)的結(jié)構(gòu)體成員字段的反射值對(duì)象 |
| NumField() int | 返回結(jié)構(gòu)體成員字段數(shù)量 |
| FieldByName(name string) Value | 根據(jù)給定字符串返回字符串對(duì)應(yīng)的結(jié)構(gòu)體字段,沒有找到時(shí)返回零值 |
| FieldByIndex(index []int) Value | 多層成員訪問時(shí),根據(jù) []int 提供的每個(gè)結(jié)構(gòu)體的字段索引,返回字段的值; 沒有找到時(shí)返回零值 |
| FieldByNameFunc(match func(string) bool) Value | 根據(jù)匹配函數(shù)匹配需要的字段,沒有找到時(shí)返回零值 |
空與有效性判斷
反射值對(duì)象(reflect.Value)提供一系列方法進(jìn)行零值和空判定:
| 方 法 | 說(shuō) 明 |
|---|---|
| IsNil() bool | 是否為 nil,只對(duì)通道、函數(shù)、接口、map、指針或切片有效(否則會(huì)panic) |
| IsValid() bool | 是否有效,當(dāng)值本身非法時(shí)(不包含任何值,或值為 nil),返回 false |
修改值
通過(guò)反射修改變量值的前提條件之一:這個(gè)值必須可以被尋址,簡(jiǎn)單地說(shuō)就是這個(gè)變量必須能被修改。結(jié)構(gòu)體成員中,如果字段沒有被導(dǎo)出,即便也可以被訪問,也不能通過(guò)反射修改。
| 方法名 | 備 注 |
|---|---|
| Elem() Value | 取值指向的元素值(類似于*操作);對(duì)指針或接口時(shí)發(fā)生panic |
| Addr() Value | 對(duì)可尋址的值返回其地址(類似于&操作);當(dāng)值不可尋址時(shí)發(fā)生panic |
| CanAddr() bool | 表示值是否可尋址 |
| CanSet() bool | 返回值能否被修改;要求值可尋址且是導(dǎo)出的字段 |
修改結(jié)構(gòu)體字段的值(需要結(jié)構(gòu)體地址,與導(dǎo)出字段):
func reflectModifyValue() {
type Dog struct {
LegCount int
}
// 獲取dog實(shí)例地址的反射值對(duì)象
valueOfDog := reflect.ValueOf(&Dog{})
// 取出dog實(shí)例地址的元素
valueOfDog = valueOfDog.Elem()
vLegCount := valueOfDog.FieldByName("LegCount")
vLegCount.SetInt(4)
fmt.Println(vLegCount.Int())
}
函數(shù)調(diào)用
如果反射值對(duì)象(reflect.Value)為函數(shù),可以通過(guò)Call()調(diào)用:參數(shù)使用反射值對(duì)象的切片[]reflect.Value構(gòu)造后傳入,返回值通過(guò)[]reflect.Value返回。
func add(a, b int) int {
return a + b
}
func reflectFunction() {
// 將函數(shù)包裝為反射值對(duì)象
funcValue := reflect.ValueOf(add)
// 構(gòu)造函數(shù)參數(shù), 傳入兩個(gè)整型值
paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
// 反射調(diào)用函數(shù)
retList := funcValue.Call(paramList)
// 獲取第一個(gè)返回值, 取整數(shù)值
fmt.Println(retList[0].Int())
}
反射三定律
官方提供了三條定律來(lái)說(shuō)明反射:
- 反射可將interface類型變量轉(zhuǎn)換成反射對(duì)象;
- 反射可將反射對(duì)象還原成interface對(duì)象;
- 要修改反射對(duì)象,其值必須是可寫的(反射其指針類型);
var x float64 = 3.4
v := reflect.ValueOf(x) // v is reflext.Value
var y float64 = v.Interface().(float64)
fmt.Println("value:", y) // 3.4
值類型不能直接修改,可通過(guò)傳遞地址并通過(guò)Elem獲取后修改:
var x float64 = 3.4
v := reflect.ValueOf(&x)
v.Elem().SetFloat(7.8)
fmt.Println("x :", v.Elem().Interface()) // 7.8
interface
interface是Go實(shí)現(xiàn)抽象的一個(gè)非常強(qiáng)大的工具;當(dāng)向接口賦值時(shí),接口會(huì)存儲(chǔ)實(shí)體的類型信息;反射就是通過(guò)接口的類型信息實(shí)現(xiàn)的。
interface類型是一種特殊類型,代表方法集合;可存放任何實(shí)現(xiàn)了其方法的值(實(shí)際存放的是(value,type)對(duì))。reflect包中實(shí)現(xiàn)了反射的各種函數(shù):
- 提取interface的value的方法
reflect.ValueOf()->reflect.Value; - 提取interface的type的方法
reflect.TypeOf()->reflect.Type;
空interface類型(interface{})的方法集為空,所以可認(rèn)為任何類型都實(shí)現(xiàn)了該接口;因此其可存放任何值。
底層結(jié)構(gòu)
interface底層結(jié)構(gòu)分為iface和eface描述接口,其區(qū)別是eface為不包含任何方法的空接口。
iface
iface定義如下:
tab指向一個(gè)itab實(shí)體的指針:表示接口的類型(賦給此接口的實(shí)體類型);data指向接口具體的值:一般而言是一個(gè)指向堆內(nèi)存的指針。
type iface struct {
tab *itab
data unsafe.Pointer
}
itab結(jié)構(gòu):
_type字段描述了實(shí)體的類型:包括內(nèi)存對(duì)齊方式,大小等;inter字段則描述了接口的類型;fun字段放置是實(shí)體類中和接口方法對(duì)應(yīng)(實(shí)體中其他方法不在此處)的方法地址,以實(shí)現(xiàn)接口調(diào)用方法的動(dòng)態(tài)分派;一般在每次給接口賦值發(fā)生轉(zhuǎn)換時(shí)會(huì)更新此表。
type itab struct {
inter *interfacetype
_type *_type
link *itab
hash uint32
bad bool
inhash bool
unused [2]byte
fun [1]uintptr
}
iface結(jié)構(gòu)全貌圖:

eface
eface結(jié)構(gòu):只維護(hù)了一個(gè) _type 字段,表示空接口所承載的具體的實(shí)體類型。
type eface struct {
_type *_type
data unsafe.Pointer
}
到此這篇關(guān)于GoLang反射機(jī)制深入講解的文章就介紹到這了,更多相關(guān)Go反射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入了解Golang中的反射機(jī)制
- Go語(yǔ)言開發(fā)框架反射機(jī)制及常見函數(shù)示例詳解
- Go語(yǔ)言的反射機(jī)制詳解
- Golang學(xué)習(xí)之反射機(jī)制的用法詳解
- go語(yǔ)言通過(guò)反射獲取和設(shè)置結(jié)構(gòu)體字段值的方法
- 淺談Go語(yǔ)言中的結(jié)構(gòu)體struct & 接口Interface & 反射
- 談?wù)凣o語(yǔ)言的反射三定律
- 詳解Golang利用反射reflect動(dòng)態(tài)調(diào)用方法
- Go語(yǔ)言學(xué)習(xí)筆記之反射用法詳解
- go語(yǔ)言通過(guò)反射創(chuàng)建結(jié)構(gòu)體、賦值、并調(diào)用對(duì)應(yīng)的操作
- Go語(yǔ)言中反射的正確使用
- 揭秘Go語(yǔ)言中的反射機(jī)制
相關(guān)文章
Go 通過(guò)結(jié)構(gòu)struct實(shí)現(xiàn)接口interface的問題
這篇文章主要介紹了Go 通過(guò)結(jié)構(gòu)struct實(shí)現(xiàn)接口interface的問題,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10
Go語(yǔ)言實(shí)現(xiàn)AES加密并編寫一個(gè)命令行應(yīng)用程序
密碼學(xué)中的高級(jí)加密標(biāo)準(zhǔn)(Advanced Encryption Standard,AES),又稱Rijndael加密法,是經(jīng)常采用的一種區(qū)塊加密標(biāo)準(zhǔn)。本文就來(lái)用Go語(yǔ)言實(shí)現(xiàn)AES加密算法,需要的可以參考一下2023-02-02
go json編譯原理XJSON實(shí)現(xiàn)四則運(yùn)算
這篇文章主要為大家介紹了go json編譯原理XJSON實(shí)現(xiàn)四則運(yùn)算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
go中Excelize處理excel表實(shí)現(xiàn)帶數(shù)據(jù)校驗(yàn)的文件導(dǎo)出
本文主要介紹了go中Excelize處理excel表實(shí)現(xiàn)帶數(shù)據(jù)校驗(yàn)的文件導(dǎo)出,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
通過(guò)Golang實(shí)現(xiàn)無(wú)頭瀏覽器截圖
在Web開發(fā)中,有時(shí)需要對(duì)網(wǎng)頁(yè)進(jìn)行截圖,以便進(jìn)行頁(yè)面預(yù)覽、測(cè)試等操作,本文為大家整理了Golang實(shí)現(xiàn)無(wú)頭瀏覽器的截圖的方法,感興趣的可以了解一下2023-05-05
Golang多線程爬蟲高效抓取大量數(shù)據(jù)的利器
Golang多線程爬蟲是一種高效抓取大量數(shù)據(jù)的利器。Golang語(yǔ)言天生支持并發(fā)和多線程,可以輕松實(shí)現(xiàn)多線程爬蟲的開發(fā)。通過(guò)使用Golang的協(xié)程和通道,可以實(shí)現(xiàn)爬蟲的高效并發(fā)抓取、數(shù)據(jù)處理和存儲(chǔ)2023-05-05

