Go語(yǔ)言學(xué)習(xí)教程之反射的示例詳解
介紹

reflect包實(shí)現(xiàn)運(yùn)行時(shí)反射,允許一個(gè)程序操作任何類(lèi)型的對(duì)象。典型的使用是:取靜態(tài)類(lèi)型interface{}的值,通過(guò)調(diào)用TypeOf獲取它的動(dòng)態(tài)類(lèi)型信息,調(diào)用ValueOf會(huì)返回一個(gè)表示運(yùn)行時(shí)數(shù)據(jù)的一個(gè)值。本文通過(guò)記錄對(duì)reflect包的簡(jiǎn)單使用,來(lái)對(duì)反射有一定的了解。本文使用的Go版本:
$ go version go version go1.18 darwin/amd64
在了解反射之前,先了解以下幾個(gè)概念:
- 靜態(tài)類(lèi)型:每個(gè)變量都有一個(gè)靜態(tài)類(lèi)型,這個(gè)類(lèi)型是在編譯時(shí)(compile time)就已知且固定的。
- 動(dòng)態(tài)類(lèi)型:接口類(lèi)型的變量還有一個(gè)動(dòng)態(tài)類(lèi)型,是在運(yùn)行時(shí)(run time)分配給變量的值的一個(gè)非接口類(lèi)型。(除非分配給變量的值是
nil,因?yàn)?code>nil沒(méi)有類(lèi)型)。 - 空接口類(lèi)型
interface{}(別名any)表示空的方法集,它可以是任何值的類(lèi)型,因?yàn)槿魏沃刀紳M足有0或多個(gè)方法(有0個(gè)方法一定是任何值的子集)。 - 一個(gè)接口類(lèi)型的變量存儲(chǔ)一對(duì)內(nèi)容:分配給變量的具體的值,以及該值的類(lèi)型描述符??梢允疽庑缘乇硎緸?code>(value, type)對(duì),這里的
type是具體的類(lèi)型,而不是接口類(lèi)型。
反射的規(guī)律
1. 從接口值到反射對(duì)象的反射
在基本層面,反射只是檢測(cè)存儲(chǔ)在接口變量中的(value, type)對(duì)的一種機(jī)制。
可以使用reflect包的 reflect.ValueOf和reflect.TypeOf方法,獲取接口變量值中的(value, type)對(duì),類(lèi)型分別為reflect.Value和reflect.Type。
(1)TypeOf方法:
func TypeOf(i any) Type
TypeOf返回表示i的動(dòng)態(tài)類(lèi)型的反射Type。如果i是nil,那么返回nil。
(2)ValueOf方法:
func ValueOf(i any) Value
ValueOf返回一個(gè)新的Value,初始化為存儲(chǔ)在接口i中的具體值。ValueOf(nil) 會(huì)返回零Value。這個(gè)零Value是反射對(duì)象中表示沒(méi)有值的Value。
var a interface{} = 1
var b interface{} = 1.11
var c string = "aaa"
// 將接口類(lèi)型的變量運(yùn)行時(shí)存儲(chǔ)的具體的值和類(lèi)型顯示地獲取到
fmt.Println("type:", reflect.TypeOf(a)) // type: int
fmt.Println("value:", reflect.ValueOf(a)) // value: 1
fmt.Println("type:", reflect.TypeOf(nil)) // type: <nil>
fmt.Println("value:", reflect.ValueOf(nil)) // value: <invalid reflect.Value>
fmt.Println("type:", reflect.TypeOf(b)) // type: float64
fmt.Println("value:", reflect.ValueOf(b)) // value: 1.11
fmt.Println("type:", reflect.TypeOf(c)) // type: string
fmt.Println("value:", reflect.ValueOf(c)) // value: aaa
reflect.Value 的 Type方法,返回一個(gè) reflect.Value的類(lèi)型。
reflect.Value的String方法,將reflect.Value的底層值作為字符串返回。
fmt.Printf使用了反射:
%T使用reflect.TypeOf,拿到變量的動(dòng)態(tài)類(lèi)型。%v深入到reflect.Value內(nèi)部拿到變量具體的值。
var a interface{} = 1
fmt.Println("type:", reflect.ValueOf(a).Type()) // type: int
fmt.Println("string:", reflect.ValueOf(a).String()) // string: <int Value>
fmt.Printf("type: %T \n", a) // type: int
fmt.Printf("string: %v \n", a) // string: 1
Type 和 Value都有一個(gè) Kind 方法,返回一個(gè)表示存儲(chǔ)的項(xiàng)的類(lèi)型的常量。 比如Uint, Float64, Slice等。
Value的類(lèi)似Int和Float這種名稱(chēng)的方法能夠獲取存儲(chǔ)在內(nèi)部的值。
Value 的“getter”(取值) 和 “setter”(設(shè)置值)會(huì)對(duì)能保存該值的最大類(lèi)型進(jìn)行操作。比如對(duì)于所有有符號(hào)整數(shù),都是int64。
var a interface{} = 1
var b interface{} = 1.11
reflectA := reflect.ValueOf(a)
fmt.Println("kind: ", reflectA.Kind()) // kind: int
reflectIntA := reflectA.Int() // 返回的是 能存儲(chǔ)有符號(hào)整數(shù)的最大類(lèi)型 的值
reflectFloatB := reflect.ValueOf(b).Float() // 返回的是 能存儲(chǔ)浮點(diǎn)數(shù)的最大類(lèi)型 的值
var a1 int64 = reflectIntA
var b1 float64 = reflectFloatB
// var a2 int32 = reflectIntA // 會(huì)報(bào)錯(cuò):cannot use reflectIntA (variable of type int64) as int32 value in variable
// var b2 float32 = reflectFloatB // 會(huì)報(bào)錯(cuò):cannot use reflectFloatB (variable of type float64) as float32 value in variable
fmt.Println("a1: ", a1, "b1: ", b1) // a1: 1 b1: 1.11
2. 從反射對(duì)象到接口值的反射
像物理反射一樣,Go 中的反射產(chǎn)生了它自己的逆。可以使用reflect.Value的Interface方法還原一個(gè)接口值。Interface() 將類(lèi)型和值信息打包回一個(gè)接口表示。
Interface方法:
func (v Value) Interface() (i any)
Interface 將v的當(dāng)前值作為一個(gè)interface{}返回。它等同于:
var i interface{} = (v 的底層值)
練習(xí)代碼:
var a interface{} = 1
reflectA := reflect.ValueOf(a)
var a3 interface{} = reflectA.Interface()
var a4 int = reflectA.Interface().(int) // 使用接口值的類(lèi)型斷言
fmt.Println(a3, a4) // 1 1 ,因?yàn)閒mt.Println接收接口類(lèi)型interface{}的參數(shù),使用 reflect.Value 拿到具體的值,所以打印出運(yùn)行時(shí)的具體結(jié)果
3. 要修改反射對(duì)象,該值一定是可設(shè)置的
可設(shè)置性是reflect.Value的一個(gè)屬性,表示一個(gè)反射對(duì)象可以修改用于創(chuàng)建該反射對(duì)象的實(shí)際存儲(chǔ)??稍O(shè)置性可以通過(guò)CanSet方法獲得。如果對(duì)不可設(shè)置的reflect.Value調(diào)用Set方法,就會(huì)報(bào)錯(cuò)。
使用reflect.Value 類(lèi)型的Elem方法能通過(guò)指針間接尋址得到一個(gè)可設(shè)置性為真的reflect.Value。
var d float64 = 2.222 fmt.Println(reflect.ValueOf(d).CanSet()) // false reflectD := reflect.ValueOf(&d).Elem() fmt.Println(reflectD.CanSet()) // true reflectD.SetFloat(3.33) fmt.Println(d, reflectD) // 3.33 3.33
reflect.ValueOf(d) 是通過(guò)復(fù)制d中的內(nèi)容得到的reflect.Value類(lèi)型的值,它復(fù)制的內(nèi)容存放的內(nèi)存地址 和d的值存放的內(nèi)存地址是不同的。所以不能通過(guò)它來(lái)修改d中原本存儲(chǔ)的內(nèi)容。
上面的代碼中可以看到,調(diào)用SetFloat(3.33)之后,反射對(duì)象reflectD和創(chuàng)建該反射對(duì)象的d都發(fā)生了改變。
使用反射修改結(jié)構(gòu)體的字段:
type T struct {
A int
B string
}
t := T{111, "xxx"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()
for i := 0; i < s.NumField(); i++ { // NumField 返回結(jié)構(gòu)體中的字段數(shù)量
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n",
i,
typeOfT.Field(i).Name, // 獲取第i個(gè)字段的名稱(chēng)
f.Type(), // 獲取第i個(gè)字段的類(lèi)型
f.Interface(), // 將第i個(gè)字段轉(zhuǎn)換回接口類(lèi)型的值
)
// 0: A int = 111
// 1: B string = xxx
}
s.Field(0).SetInt(222) // 設(shè)置結(jié)構(gòu)體的第一個(gè)字段的值
s.Field(1).SetString("yyy") // 設(shè)置結(jié)構(gòu)體的第二個(gè)字段的值
fmt.Println(t) // {222 yyy}
上述練習(xí)代碼都在一個(gè)reflect.go文件中,練習(xí)時(shí)在終端執(zhí)行go run reflect.go運(yùn)行該文件。
以上就是Go語(yǔ)言學(xué)習(xí)教程之反射的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言 反射的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Go語(yǔ)言中的反射原理解析與應(yīng)用
- Go語(yǔ)言的反射reflect使用大全
- 揭秘Go語(yǔ)言中的反射機(jī)制
- go語(yǔ)言反射的基礎(chǔ)教程示例
- Go語(yǔ)言通過(guò)反射實(shí)現(xiàn)獲取各種類(lèi)型變量的值
- 一文初探Go語(yǔ)言中的reflect反射包
- 詳解如何讓Go語(yǔ)言中的反射加快
- Go語(yǔ)言的反射機(jī)制詳解
- Go語(yǔ)言反射獲取類(lèi)型屬性和方法示例
- Go語(yǔ)言學(xué)習(xí)之反射的用法詳解
- Go語(yǔ)言基礎(chǔ)反射示例詳解
- go語(yǔ)言中反射機(jī)制的三種使用場(chǎng)景
相關(guān)文章
Go語(yǔ)言中內(nèi)建函數(shù)make的使用
在Go語(yǔ)言編程中,make函數(shù)是一個(gè)重要的內(nèi)建函數(shù),它用于創(chuàng)建和初始化切片、映射和通道,握 make 的使用方法,可以幫助我們更高效地管理內(nèi)存和數(shù)據(jù)結(jié)構(gòu)2024-09-09
解析Golang中引用類(lèi)型是否進(jìn)行引用傳遞
這篇文章主要為大家介紹了Golang中引用類(lèi)型是否進(jìn)行引用傳遞剖析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Go微服務(wù)項(xiàng)目配置文件的定義和讀取示例詳解
這篇文章主要為大家介紹了Go微服務(wù)項(xiàng)目配置文件的定義和讀取示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
intelliJ?idea安裝go開(kāi)發(fā)環(huán)境并搭建go項(xiàng)目(打包)全過(guò)程
最近在配置idea開(kāi)發(fā)go語(yǔ)言時(shí)碰到很多問(wèn)題,所以這里給大家總結(jié)下,這篇文章主要給大家介紹了關(guān)于intelliJ?idea安裝go開(kāi)發(fā)環(huán)境并搭建go項(xiàng)目(打包)的相關(guān)資料,需要的朋友可以參考下2023-10-10
Go調(diào)用C++動(dòng)態(tài)庫(kù)實(shí)現(xiàn)車(chē)牌識(shí)別的示例代碼
本文主要介紹了如何利用C++中Opencv、TensorRT等庫(kù)編譯出動(dòng)態(tài)庫(kù)供Go調(diào)用,再寫(xiě)個(gè)簡(jiǎn)單的api對(duì)上傳的車(chē)輛圖片進(jìn)行車(chē)牌識(shí)別,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12
Go實(shí)現(xiàn)用戶每日限額的方法(例一天只能領(lǐng)三次福利)
這篇文章主要介紹了Go實(shí)現(xiàn)用戶每日限額的方法(例一天只能領(lǐng)三次福利)2022-01-01
一文搞懂Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)strconv
strconv包實(shí)現(xiàn)了基本數(shù)據(jù)類(lèi)型和其字符串表示的相互轉(zhuǎn)換,本文主要介紹Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)strconv,想要學(xué)習(xí)strconv標(biāo)準(zhǔn)庫(kù)的可以了解一下2023-04-04

