欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2022年09月28日 09:53:41   作者:任沫  
這篇文章主要通過記錄對(duì)reflect包的簡(jiǎn)單使用,來對(duì)反射有一定的了解。文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定幫助,需要的可以參考一下

介紹

reflect包實(shí)現(xiàn)運(yùn)行時(shí)反射,允許一個(gè)程序操作任何類型的對(duì)象。典型的使用是:取靜態(tài)類型interface{}的值,通過調(diào)用TypeOf獲取它的動(dòng)態(tài)類型信息,調(diào)用ValueOf會(huì)返回一個(gè)表示運(yùn)行時(shí)數(shù)據(jù)的一個(gè)值。本文通過記錄對(duì)reflect包的簡(jiǎn)單使用,來對(duì)反射有一定的了解。本文使用的Go版本:

$ go version
go version go1.18 darwin/amd64

在了解反射之前,先了解以下幾個(gè)概念:

  • 靜態(tài)類型:每個(gè)變量都有一個(gè)靜態(tài)類型,這個(gè)類型是在編譯時(shí)(compile time)就已知且固定的。
  • 動(dòng)態(tài)類型:接口類型的變量還有一個(gè)動(dòng)態(tài)類型,是在運(yùn)行時(shí)(run time)分配給變量的值的一個(gè)非接口類型。(除非分配給變量的值是nil,因?yàn)?code>nil沒有類型)。
  • 空接口類型interface{} (別名any)表示空的方法集,它可以是任何值的類型,因?yàn)槿魏沃刀紳M足有0或多個(gè)方法(有0個(gè)方法一定是任何值的子集)。
  • 一個(gè)接口類型的變量存儲(chǔ)一對(duì)內(nèi)容:分配給變量的具體的值,以及該值的類型描述符。可以示意性地表示為(value, type)對(duì),這里的type是具體的類型,而不是接口類型。

反射的規(guī)律

1. 從接口值到反射對(duì)象的反射

在基本層面,反射只是檢測(cè)存儲(chǔ)在接口變量中的(value, type)對(duì)的一種機(jī)制。

可以使用reflect包的 reflect.ValueOfreflect.TypeOf方法,獲取接口變量值中的(value, type)對(duì),類型分別為reflect.Valuereflect.Type。

(1)TypeOf方法:

func TypeOf(i any) Type

TypeOf返回表示i的動(dòng)態(tài)類型的反射Type。如果inil,那么返回nil。

(2)ValueOf方法:

func ValueOf(i any) Value

ValueOf返回一個(gè)新的Value,初始化為存儲(chǔ)在接口i中的具體值。ValueOf(nil) 會(huì)返回零Value。這個(gè)零Value是反射對(duì)象中表示沒有值的Value。

var a interface{} = 1
var b interface{} = 1.11
var c string = "aaa"

// 將接口類型的變量運(yùn)行時(shí)存儲(chǔ)的具體的值和類型顯示地獲取到
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的類型。

reflect.ValueString方法,將reflect.Value的底層值作為字符串返回。

fmt.Printf使用了反射:

  • %T使用reflect.TypeOf,拿到變量的動(dòng)態(tà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)的類型的常量。 比如UintFloat64Slice等。

Value的類似IntFloat這種名稱的方法能夠獲取存儲(chǔ)在內(nèi)部的值。

Value 的“getter”(取值) 和 “setter”(設(shè)置值)會(huì)對(duì)能保存該值的最大類型進(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ù)的最大類型 的值
reflectFloatB := reflect.ValueOf(b).Float() // 返回的是 能存儲(chǔ)浮點(diǎn)數(shù)的最大類型 的值

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.ValueInterface方法還原一個(gè)接口值。Interface() 將類型和值信息打包回一個(gè)接口表示。

Interface方法:

func (v Value) Interface() (i any)

Interfacev的當(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) // 使用接口值的類型斷言

fmt.Println(a3, a4) // 1 1 ,因?yàn)閒mt.Println接收接口類型interface{}的參數(shù),使用 reflect.Value 拿到具體的值,所以打印出運(yùn)行時(shí)的具體結(jié)果

3. 要修改反射對(duì)象,該值一定是可設(shè)置的

可設(shè)置性是reflect.Value的一個(gè)屬性,表示一個(gè)反射對(duì)象可以修改用于創(chuàng)建該反射對(duì)象的實(shí)際存儲(chǔ)??稍O(shè)置性可以通過CanSet方法獲得。如果對(duì)不可設(shè)置的reflect.Value調(diào)用Set方法,就會(huì)報(bào)錯(cuò)。

使用reflect.Value 類型的Elem方法能通過指針間接尋址得到一個(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) 是通過復(fù)制d中的內(nèi)容得到的reflect.Value類型的值,它復(fù)制的內(nèi)容存放的內(nèi)存地址 和d的值存放的內(nè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è)字段的名稱
        f.Type(),              // 獲取第i個(gè)字段的類型
        f.Interface(),         // 將第i個(gè)字段轉(zhuǎn)換回接口類型的值
    )
    // 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)文章!

相關(guān)文章

  • Go語(yǔ)言跨平臺(tái)時(shí)字符串中的換行符如何統(tǒng)一?

    Go語(yǔ)言跨平臺(tái)時(shí)字符串中的換行符如何統(tǒng)一?

    本文介紹了Go語(yǔ)言中統(tǒng)一換行符的方法,包括使用`strings.ReplaceAll`函數(shù)將Windows風(fēng)格的換行符`\r\n`替換為Unix風(fēng)格的換行符`\n`,或?qū)\n`替換為`\r\n`,統(tǒng)一換行符可以避免不同平臺(tái)間顯示不一致、傳輸時(shí)出現(xiàn)多余的換行符或丟失換行符,以及解析錯(cuò)誤等問題
    2024-11-11
  • golang程序使用alpine編譯出最小arm鏡像實(shí)現(xiàn)

    golang程序使用alpine編譯出最小arm鏡像實(shí)現(xiàn)

    這篇文章主要為大家介紹了golang程序使用alpine編譯出最小arm鏡像,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang并發(fā)編程的實(shí)現(xiàn)

    golang并發(fā)編程的實(shí)現(xiàn)

    這篇文章主要介紹了golang并發(fā)編程的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 淺談Go Slice 高級(jí)實(shí)踐

    淺談Go Slice 高級(jí)實(shí)踐

    這篇文章主要介紹了淺談Go Slice 高級(jí)實(shí)踐,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Go語(yǔ)言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別介紹

    Go語(yǔ)言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別介紹

    這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言中結(jié)構(gòu)體方法副本傳參與指針傳參的區(qū)別的相關(guān)資料,文中先對(duì)GO語(yǔ)言結(jié)構(gòu)體方法跟結(jié)構(gòu)體指針方法的區(qū)別進(jìn)行了一些簡(jiǎn)單的介紹,來幫助大家理解學(xué)習(xí),需要的朋友可以參考下。
    2017-12-12
  • 詳解如何在Go中如何編寫出可測(cè)試的代碼

    詳解如何在Go中如何編寫出可測(cè)試的代碼

    在編寫測(cè)試代碼之前,還有一個(gè)很重要的點(diǎn),容易被忽略,就是什么樣的代碼是可測(cè)試的代碼,所以本文就來聊一聊在?Go?中如何寫出可測(cè)試的代碼吧
    2023-08-08
  • 一文帶你掌握Golang基礎(chǔ)之通道

    一文帶你掌握Golang基礎(chǔ)之通道

    在Java中,多線程之間的通信方式有哪些?記得嗎?Java多線程間通信的解決方案有很多種,比如:synchronized。在go中,就一種:通道,文中介紹的非常詳細(xì),感興趣的同學(xué)可以參考下
    2023-05-05
  • 從零封裝Gin框架配置初始化全局變量

    從零封裝Gin框架配置初始化全局變量

    這篇文章主要為大家介紹了從零封裝Gin框架配置初始化全局變量,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Golang Map類型的使用(增刪查改)

    Golang Map類型的使用(增刪查改)

    在Go中,map是哈希表的引用,是一種key-value數(shù)據(jù)結(jié)構(gòu),本文主要介紹了Golang Map類型的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-05-05
  • go code review 代碼調(diào)試

    go code review 代碼調(diào)試

    這篇文章主要為大家介紹了go code review 代碼調(diào)試方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論