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

Go利用反射reflect實(shí)現(xiàn)獲取接口變量信息

 更新時(shí)間:2022年05月28日 15:14:48   作者:starine  
反射是通過(guò)實(shí)體對(duì)象獲取反射對(duì)象(Value、Type),然后可以操作相應(yīng)的方法。本文將利用Go語(yǔ)言中的反射reflect實(shí)現(xiàn)獲取接口變量信息,需要的可以參考一下

引言

反射是通過(guò)實(shí)體對(duì)象獲取反射對(duì)象(Value、Type),然后可以操作相應(yīng)的方法。在某些情況下,我們可能并不知道變量的具體類(lèi)型,這時(shí)候就可以用反射來(lái)獲取這個(gè)變量的類(lèi)型或者方法。

一、反射的規(guī)則

其實(shí)反射的操作步驟非常的簡(jiǎn)單,就是通過(guò)實(shí)體對(duì)象獲取反射對(duì)象(Value、Type),然后操作相應(yīng)的方法即可。

下圖描述了實(shí)例、Value、Type 三者之間的轉(zhuǎn)換關(guān)系:

反射 API 的分類(lèi)總結(jié)如下:

1、從實(shí)例到 Value

通過(guò)實(shí)例獲取 Value 對(duì)象,直接使用 reflect.ValueOf() 函數(shù)。例如:

func ValueOf(i interface {}) Value

2、從實(shí)例到 Type

通過(guò)實(shí)例獲取反射對(duì)象的 Type,直接使用 reflect.TypeOf() 函數(shù)。例如:

func TypeOf(i interface{}) Type

3、從 Type 到 Value

Type 里面只有類(lèi)型信息,所以直接從一個(gè) Type 接口變量里面是無(wú)法獲得實(shí)例的 Value 的,但可以通過(guò)該 Type 構(gòu)建一個(gè)新實(shí)例的 Value。reflect 包提供了兩種方法,示例如下:

//New 返回的是一個(gè) Value,該 Value 的 type 為 PtrTo(typ),即 Value 的 Type 是指定 typ 的指針類(lèi)型
func New(typ Type) Value
//Zero 返回的是一個(gè) typ 類(lèi)型的零佳,注意返回的 Value 不能尋址,位不可改變
func Zero(typ Type) Value

如果知道一個(gè)類(lèi)型值的底層存放地址,則還有一個(gè)函數(shù)是可以依據(jù) type 和該地址值恢復(fù)出 Value 的。例如:

func NewAt(typ Type, p unsafe.Pointer) Value

4、從 Value 到 Type

從反射對(duì)象 Value 到 Type 可以直接調(diào)用 Value 的方法,因?yàn)?Value 內(nèi)部存放著到 Type 類(lèi)型的指針。例如:

func (v Value) Type() Type

5、從 Value 到實(shí)例

Value 本身就包含類(lèi)型和值信息,reflect 提供了豐富的方法來(lái)實(shí)現(xiàn)從 Value 到實(shí)例的轉(zhuǎn)換。例如:

//該方法最通用,用來(lái)將 Value 轉(zhuǎn)換為空接口,該空接口內(nèi)部存放具體類(lèi)型實(shí)例
//可以使用接口類(lèi)型查詢(xún)?nèi)ミ€原為具體的類(lèi)型
func (v Value) Interface() (i interface{})

//Value 自身也提供豐富的方法,直接將 Value 轉(zhuǎn)換為簡(jiǎn)單類(lèi)型實(shí)例,如果類(lèi)型不匹配,則直接引起 panic
func (v Value) Bool () bool
func (v Value) Float() float64
func (v Value) Int() int64
func (v Value) Uint() uint64

6、從 Value 的指針到值

從一個(gè)指針類(lèi)型的 Value 獲得值類(lèi)型 Value 有兩種方法,示例如下。

//如果 v 類(lèi)型是接口,則 Elem() 返回接口綁定的實(shí)例的 Value,如采 v 類(lèi)型是指針,則返回指針值的 Value,否則引起 panic
func (v Value) Elem() Value
//如果 v 是指針,則返回指針值的 Value,否則返回 v 自身,該函數(shù)不會(huì)引起 panic
func Indirect(v Value) Value

7、Type 指針和值的相互轉(zhuǎn)換

指針類(lèi)型 Type 到值類(lèi)型 Type。例如:

//t 必須是 Array、Chan、Map、Ptr、Slice,否則會(huì)引起 panic
//Elem 返回的是其內(nèi)部元素的 Type
t.Elem() Type

值類(lèi)型 Type 到指針類(lèi)型 Type。例如:

//PtrTo 返回的是指向 t 的指針型 Type
func PtrTo(t Type) Type

8、Value 值的可修改性

Value 值的修改涉及如下兩個(gè)方法:

//通過(guò) CanSet 判斷是否能修改
func (v Value ) CanSet() bool
//通過(guò) Set 進(jìn)行修改
func (v Value ) Set(x Value)

Value 值在什么情況下可以修改?我們知道實(shí)例對(duì)象傳遞給接口的是一個(gè)完全的值拷貝,如果調(diào)用反射的方法 reflect.ValueOf() 傳進(jìn)去的是一個(gè)值類(lèi)型變量, 則獲得的 Value 實(shí)際上是原對(duì)象的一個(gè)副本,這個(gè) Value 是無(wú)論如何也不能被修改的。

9、根據(jù) Go 官方關(guān)于反射的文檔,反射有三大定律:9

  • Reflection goes from interface value to reflection object.
  • Reflection goes from reflection object to interface value.
  • To modify a reflection object, the value must be settable.

第一條是最基本的:反射可以從接口值得到反射對(duì)象。

反射是一種檢測(cè)存儲(chǔ)在 interface中的類(lèi)型和值機(jī)制。這可以通過(guò) TypeOf函數(shù)和 ValueOf函數(shù)得到。

第二條實(shí)際上和第一條是相反的機(jī)制,反射可以從反射對(duì)象獲得接口值。

它將 ValueOf的返回值通過(guò) Interface()函數(shù)反向轉(zhuǎn)變成 interface變量。

前兩條就是說(shuō) 接口型變量和 反射類(lèi)型對(duì)象可以相互轉(zhuǎn)化,反射類(lèi)型對(duì)象實(shí)際上就是指的前面說(shuō)的 reflect.Type和 reflect.Value。

第三條不太好懂:如果需要操作一個(gè)反射變量,則其值必須可以修改。

反射變量可設(shè)置的本質(zhì)是它存儲(chǔ)了原變量本身,這樣對(duì)反射變量的操作,就會(huì)反映到原變量本身;反之,如果反射變量不能代表原變量,那么操作了反射變量,不會(huì)對(duì)原變量產(chǎn)生任何影響,這會(huì)給使用者帶來(lái)疑惑。所以第二種情況在語(yǔ)言層面是不被允許的。

二、反射的使用

從relfect.Value中獲取接口interface的信息

當(dāng)執(zhí)行reflect.ValueOf(interface)之后,就得到了一個(gè)類(lèi)型為”relfect.Value”變量,可以通過(guò)它本身的Interface()方法獲得接口變量的真實(shí)內(nèi)容,然后可以通過(guò)類(lèi)型判斷進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換為原有真實(shí)類(lèi)型。不過(guò),我們可能是已知原有類(lèi)型,也有可能是未知原有類(lèi)型,因此,下面分兩種情況進(jìn)行說(shuō)明。

1、已知原有類(lèi)型

已知類(lèi)型后轉(zhuǎn)換為其對(duì)應(yīng)的類(lèi)型的做法如下,直接通過(guò)Interface方法然后強(qiáng)制轉(zhuǎn)換,如下:

realValue := value.Interface().(已知的類(lèi)型)

示例代碼:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var num float64 = 3.1415926

    pointer := reflect.ValueOf(&num)
    value := reflect.ValueOf(num)

    // 可以理解為“強(qiáng)制轉(zhuǎn)換”,但是需要注意的時(shí)候,轉(zhuǎn)換的時(shí)候,如果轉(zhuǎn)換的類(lèi)型不完全符合,則直接panic
    // Golang 對(duì)類(lèi)型要求非常嚴(yán)格,類(lèi)型一定要完全符合
    // 如下兩個(gè),一個(gè)是*float64,一個(gè)是float64,如果弄混,則會(huì)panic
    convertPointer := pointer.Interface().(*float64)
    convertValue := value.Interface().(float64)

    fmt.Println(convertPointer)
    fmt.Println(convertValue)
}

運(yùn)行結(jié)果:

0xc000018080
3.1415926

說(shuō)明

  • 轉(zhuǎn)換的時(shí)候,如果轉(zhuǎn)換的類(lèi)型不完全符合,則直接panic,類(lèi)型要求非常嚴(yán)格!
  • 轉(zhuǎn)換的時(shí)候,要區(qū)分是指針還是指
  • 也就是說(shuō)反射可以將“反射類(lèi)型對(duì)象”再重新轉(zhuǎn)換為“接口類(lèi)型變量”

2、未知原有類(lèi)型

很多情況下,我們可能并不知道其具體類(lèi)型,那么這個(gè)時(shí)候,該如何做呢?需要我們進(jìn)行遍歷探測(cè)其Filed來(lái)得知,示例如下:

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string
    Age int
    Sex string
}

func (p Person)Say(msg string)  {
    fmt.Println("hello,",msg)
}
func (p Person)PrintInfo()  {
    fmt.Printf("姓名:%s,年齡:%d,性別:%s\n",p.Name,p.Age,p.Sex)
}

func main() {
    p1 := Person{"王富貴",20,"男"}

    DoFiledAndMethod(p1)

}

// 通過(guò)接口來(lái)獲取任意參數(shù)
func DoFiledAndMethod(input interface{}) {

    getType := reflect.TypeOf(input) //先獲取input的類(lèi)型
    fmt.Println("get Type is :", getType.Name()) // Person
    fmt.Println("get Kind is : ", getType.Kind()) // struct

    getValue := reflect.ValueOf(input)
    fmt.Println("get all Fields is:", getValue) //{王富貴 20 男}

    // 獲取方法字段
    // 1. 先獲取interface的reflect.Type,然后通過(guò)NumField進(jìn)行遍歷
    // 2. 再通過(guò)reflect.Type的Field獲取其Field
    // 3. 最后通過(guò)Field的Interface()得到對(duì)應(yīng)的value
    for i := 0; i < getType.NumField(); i++ {
        field := getType.Field(i)
        value := getValue.Field(i).Interface() //獲取第i個(gè)值
        fmt.Printf("字段名稱(chēng):%s, 字段類(lèi)型:%s, 字段數(shù)值:%v \n", field.Name, field.Type, value)
    }

    // 通過(guò)反射,操作方法
    // 1. 先獲取interface的reflect.Type,然后通過(guò).NumMethod進(jìn)行遍歷
    // 2. 再公國(guó)reflect.Type的Method獲取其Method
    for i := 0; i < getType.NumMethod(); i++ {
        method := getType.Method(i)
        fmt.Printf("方法名稱(chēng):%s, 方法類(lèi)型:%v \n", method.Name, method.Type)
    }
}

運(yùn)行結(jié)果:

get Type is : Person
get Kind is :  struct
get all Fields is: {王富貴 20 男}
字段名稱(chēng):Name, 字段類(lèi)型:string, 字段數(shù)值:王富貴 
字段名稱(chēng):Age, 字段類(lèi)型:int, 字段數(shù)值:20 
字段名稱(chēng):Sex, 字段類(lèi)型:string, 字段數(shù)值:男 
方法名稱(chēng):PrintInfo, 方法類(lèi)型:func(main.Person) 
方法名稱(chēng):Say, 方法類(lèi)型:func(main.Person, string) 

總結(jié)

獲取未知類(lèi)型的interface的具體變量及其類(lèi)型的步驟為:

  • 先獲取interface的reflect.Type,然后通過(guò)NumField進(jìn)行遍歷
  • 再通過(guò)reflect.Type的Field獲取其Field
  • 最后通過(guò)Field的Interface()得到對(duì)應(yīng)的value

獲取未知類(lèi)型的interface的所屬方法(函數(shù))的步驟為:

  • 先獲取interface的reflect.Type,然后通過(guò)NumMethod進(jìn)行遍歷
  • 再分別通過(guò)reflect.Type的Method獲取對(duì)應(yīng)的真實(shí)的方法(函數(shù))
  • 最后對(duì)結(jié)果取其N(xiāo)ame和Type得知具體的方法名
  • 也就是說(shuō)反射可以將“反射類(lèi)型對(duì)象”再重新轉(zhuǎn)換為“接口類(lèi)型變量”
  • struct 或者 struct 的嵌套都是一樣的判斷處理方式

以上就是Go利用反射reflect實(shí)現(xiàn)獲取接口變量信息的詳細(xì)內(nèi)容,更多關(guān)于Go reflect獲取接口信息的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Go語(yǔ)言實(shí)現(xiàn)Yaml編碼和解碼的方法詳解

    使用Go語(yǔ)言實(shí)現(xiàn)Yaml編碼和解碼的方法詳解

    在這篇文章中,我們將介紹如何使用Go語(yǔ)言編寫(xiě)代碼來(lái)實(shí)現(xiàn)Yaml編碼和解碼,文中有詳細(xì)的代碼示例供大家參考,對(duì)大家的學(xué)習(xí)和工作有一定的幫助,需要的朋友可以參考下
    2023-11-11
  • ?Go?語(yǔ)言實(shí)現(xiàn)?HTTP?文件上傳和下載

    ?Go?語(yǔ)言實(shí)現(xiàn)?HTTP?文件上傳和下載

    這篇文章主要介紹了Go語(yǔ)言實(shí)現(xiàn)HTTP文件上傳和下載,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • Golang并發(fā)繞不開(kāi)的重要組件之Goroutine詳解

    Golang并發(fā)繞不開(kāi)的重要組件之Goroutine詳解

    Goroutine、Channel、Context、Sync都是Golang并發(fā)編程中的幾個(gè)重要組件,這篇文中主要為大家介紹了Goroutine的相關(guān)知識(shí),需要的可以參考一下
    2023-06-06
  • Go語(yǔ)言學(xué)習(xí)之WaitGroup用法詳解

    Go語(yǔ)言學(xué)習(xí)之WaitGroup用法詳解

    Go語(yǔ)言中的?WaitGroup?和?Java?中的?CyclicBarrier、CountDownLatch?非常類(lèi)似。本文將詳細(xì)為大家講講WaitGroup的用法,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2022-06-06
  • Go語(yǔ)言開(kāi)發(fā)技巧必知的小細(xì)節(jié)提升效率

    Go語(yǔ)言開(kāi)發(fā)技巧必知的小細(xì)節(jié)提升效率

    這篇文章主要介紹了Go語(yǔ)言開(kāi)發(fā)技巧必知的小細(xì)節(jié)提升效率,分享幾個(gè)你可能不知道的Go語(yǔ)言小細(xì)節(jié),希望能幫助大家更好地學(xué)習(xí)這門(mén)語(yǔ)言
    2024-01-01
  • Go如何優(yōu)雅的關(guān)閉goroutine協(xié)程

    Go如何優(yōu)雅的關(guān)閉goroutine協(xié)程

    本文將介紹首先為什么需要主動(dòng)關(guān)閉goroutine,并介紹如何在Go語(yǔ)言中關(guān)閉goroutine的常見(jiàn)套路,包括傳遞終止信號(hào)和協(xié)程內(nèi)部捕捉終止信號(hào),之后,文章列舉了需要主動(dòng)關(guān)閉協(xié)程運(yùn)行的常見(jiàn)場(chǎng)景,希望通過(guò)本文的介紹,讀者能夠掌握如何在適當(dāng)?shù)臅r(shí)候關(guān)閉goroutine
    2023-05-05
  • Go語(yǔ)言按字節(jié)截取字符串的方法

    Go語(yǔ)言按字節(jié)截取字符串的方法

    這篇文章主要介紹了Go語(yǔ)言按字節(jié)截取字符串的方法,涉及Go語(yǔ)言操作字符串的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-02-02
  • 深入探索Golang中的SM4加密解密算法

    深入探索Golang中的SM4加密解密算法

    SM4加密算法在安全性、高效性、簡(jiǎn)單性、標(biāo)準(zhǔn)化和廣泛支持等方面具有優(yōu)勢(shì),適用于各種數(shù)據(jù)保護(hù)和加密應(yīng)用場(chǎng)景,這篇文章就來(lái)和大家探索一下Golang中的SM4加密解密算法吧
    2023-06-06
  • Golang如何自定義logrus日志保存為日志文件

    Golang如何自定義logrus日志保存為日志文件

    這篇文章主要給大家介紹了關(guān)于Golang如何自定義logrus日志保存為日志文件的相關(guān)資料,logrus是目前Github上star數(shù)量最多的日志庫(kù),logrus功能強(qiáng)大,性能高效,而且具有高度靈活性,提供了自定義插件的功能,很多開(kāi)源項(xiàng)目都是用了logrus來(lái)記錄其日志,需要的朋友可以參考下
    2024-02-02
  • Golang并發(fā)繞不開(kāi)的重要組件之Channel詳解

    Golang并發(fā)繞不開(kāi)的重要組件之Channel詳解

    Channel是一個(gè)提供可接收和發(fā)送特定類(lèi)型值的用于并發(fā)函數(shù)通信的數(shù)據(jù)類(lèi)型,也是Golang并發(fā)繞不開(kāi)的重要組件之一,本文就來(lái)和大家深入聊聊Channel的相關(guān)知識(shí)吧
    2023-06-06

最新評(píng)論