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

GoLang反射機(jī)制深入講解

 更新時(shí)間:2022年12月23日 09:43:51   作者:alwaysrun  
這篇文章主要介紹了GoLang反射機(jī)制,反射是一種讓程序可以在運(yùn)行時(shí)( runtime )檢查其數(shù)據(jù)結(jié)構(gòu)的能力,通過反射可以獲取豐富的類型信息

反射

Go語言提供了reflect 包來訪問程序的反射信息;定義了兩個(gè)重要的類型Type和Value:

  • reflect.TypeOf:獲取任意值的類型對(duì)象(reflect.Type);
  • reflect.ValueOf:獲得值的反射值對(duì)象(reflect.Value);

反射類型Type

Go語言程序中的類型(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                 // 無符號(hào)整型
    Uint8                // 無符號(hào)8位整型
    Uint16               // 無符號(hào)16位整型
    Uint32               // 無符號(hào)32位整型
    Uint64               // 無符號(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ì)象,可通過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ì)象信息后,可通過NumField() 和 Field() 方法獲得結(jié)構(gòu)體成員的詳細(xì)信息。

方法說明
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)
	}
	// 通過字段名, 找到字段類型信息
	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

通過下面幾種方法從反射值對(duì)象 reflect.Value 中獲取原值

方法名說 明
Interface() interface {}將值以 interface{} 類型返回,可以通過類型斷言轉(zhuǎn)換為指定類型
Int() int64將值以 int 類型返回,所有有符號(hào)整型均可以此方式返回
Uint() uint64將值以 uint 類型返回,所有無符號(hào)整型均可以此方式返回
Float() float64將值以雙精度(float64)類型返回,所有浮點(diǎn)數(shù)(float32、float64)均可以此方式返回
Bool() bool將值以 bool 類型返回
Bytes() []bytes將值以字節(jié)數(shù)組 []bytes 類型返回
String() string將值以字符串類型返回

通過反射獲取變量的值:

func reflectValue()  {
	var a int = 1024
	// 獲取變量a的反射值對(duì)象
	valueOfA := reflect.ValueOf(a)
	// 獲取interface{}類型的值, 通過類型斷言轉(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)體訪問的方法,通過這些方法可以完成對(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)行零值和空判定:

方 法說 明
IsNil() bool是否為 nil,只對(duì)通道、函數(shù)、接口、map、指針或切片有效(否則會(huì)panic)
IsValid() bool是否有效,當(dāng)值本身非法時(shí)(不包含任何值,或值為 nil),返回 false

修改值

通過反射修改變量值的前提條件之一:這個(gè)值必須可以被尋址,簡單地說就是這個(gè)變量必須能被修改。結(jié)構(gòu)體成員中,如果字段沒有被導(dǎo)出,即便也可以被訪問,也不能通過反射修改。

方法名備 注
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ù),可以通過Call()調(diào)用:參數(shù)使用反射值對(duì)象的切片[]reflect.Value構(gòu)造后傳入,返回值通過[]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())
}

反射三定律

官方提供了三條定律來說明反射:

  • 反射可將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

值類型不能直接修改,可通過傳遞地址并通過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í)體的類型信息;反射就是通過接口的類型信息實(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)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 最新版Golang?pprof使用詳解(引入、抓取、分析,圖文結(jié)合)

    最新版Golang?pprof使用詳解(引入、抓取、分析,圖文結(jié)合)

    這篇文章主要介紹了最新版Golang?pprof使用詳解包括引入、抓取、分析,圖文結(jié)合,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2024-08-08
  • 詳解Golang中零拷貝的原理以及實(shí)踐

    詳解Golang中零拷貝的原理以及實(shí)踐

    零拷貝技術(shù)相信大家都有所耳聞,但是本文不僅會(huì)講述零拷貝技術(shù)的原理,并將從實(shí)際代碼出發(fā),看看零拷貝技術(shù)在golang中的應(yīng)用,現(xiàn)在讓我們開始吧
    2023-07-07
  • golang redis中Pipeline通道的使用詳解

    golang redis中Pipeline通道的使用詳解

    本文主要介紹了golang redis中Pipeline通道的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Go中調(diào)用JS代碼(otto)的實(shí)現(xiàn)示例

    Go中調(diào)用JS代碼(otto)的實(shí)現(xiàn)示例

    Otto是一個(gè)用Go語言實(shí)現(xiàn)的JavaScript解釋器,可用于執(zhí)行和操作JavaScript代碼,適合在Go項(xiàng)目中執(zhí)行簡單的JS腳本,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • 詳解Golang中文件系統(tǒng)事件監(jiān)聽

    詳解Golang中文件系統(tǒng)事件監(jiān)聽

    文件系統(tǒng)事件是指文件系統(tǒng)相關(guān)的各種操作和狀態(tài)變化,當(dāng)一個(gè)應(yīng)用層的進(jìn)程操作文件或目錄時(shí),會(huì)觸發(fā)system call,內(nèi)核的notification子系統(tǒng)可以守在那里,把該進(jìn)程對(duì)文件的操作上報(bào)給應(yīng)用層的監(jiān)聽進(jìn)程,這篇文章主要介紹了Golang之文件系統(tǒng)事件監(jiān)聽,需要的朋友可以參考下
    2024-01-01
  • 6行代碼快速解決golang TCP粘包問題

    6行代碼快速解決golang TCP粘包問題

    在用golang開發(fā)人工客服系統(tǒng)的時(shí)候碰到了粘包問題,那么什么是粘包呢?下面這篇文章主要給大家介紹了關(guān)于如何通過6行代碼快速解決golang TCP粘包問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • golang中beego入門

    golang中beego入門

    Beego是一個(gè)基于Go語言的開源框架,用于構(gòu)建Web應(yīng)用程序和API,本文主要介紹了golang中beego入門,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • Qt6.5 grpc組件使用 + golang grpc server示例詳解

    Qt6.5 grpc組件使用 + golang grpc server

    這篇文章主要介紹了Qt6.5 grpc組件使用+golang grpc server示例,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • Go項(xiàng)目中添加生成時(shí)間與版本信息的方法

    Go項(xiàng)目中添加生成時(shí)間與版本信息的方法

    本文主要介紹了Go項(xiàng)目中添加生成時(shí)間與版本信息的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Golang切片和數(shù)組拷貝詳解(淺拷貝和深拷貝)

    Golang切片和數(shù)組拷貝詳解(淺拷貝和深拷貝)

    這篇文章主要為大家詳細(xì)介紹一下Golang切片拷貝和數(shù)組拷貝,文中有詳細(xì)的代碼示例供大家參考,需要的可以參考一下
    2023-04-04

最新評(píng)論