日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

GoLang反射機制深入講解_Golang

作者:alwaysrun ? 更新時間: 2023-01-28 編程語言

反射

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

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

反射類型Type

Go語言程序中的類型(Type)指的是系統原生數據類型(如 int、string、bool、float32 等),以及使用 type 關鍵字定義的類型;而反射種類(Kind)是指對象的歸屬分類:

type Kind uint
const (
    Invalid Kind = iota  // 非法類型
    Bool                 // 布爾型
    Int                  // 有符號整型
    Int8                 // 有符號8位整型
    Int16                // 有符號16位整型
    Int32                // 有符號32位整型
    Int64                // 有符號64位整型
    Uint                 // 無符號整型
    Uint8                // 無符號8位整型
    Uint16               // 無符號16位整型
    Uint32               // 無符號32位整型
    Uint64               // 無符號64位整型
    Uintptr              // 指針
    Float32              // 單精度浮點數
    Float64              // 雙精度浮點數
    Complex64            // 64位復數類型
    Complex128           // 128位復數類型
    Array                // 數組
    Chan                 // 通道
    Func                 // 函數
    Interface            // 接口
    Map                  // 映射
    Ptr                  // 指針
    Slice                // 切片
    String               // 字符串
    Struct               // 結構體
    UnsafePointer        // 底層指針
)

指針

對指針指向的對象,可通過reflect.Elem() 方法獲取這個指針指向的元素類型(等效于對指針類型變量做了一個*操作)。

func reflectStruct() {
	type Cat struct {
	}
	aCat := &Cat{}
	// 獲取結構體實例的反射類型對象
	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'

結構體

對結構體對象,獲取對象信息后,可通過NumField() 和 Field() 方法獲得結構體成員的詳細信息。

方法 說明
Field(i int) StructField 根據索引返回索引對應字段的信息
NumField() int 返回結構體成員字段數量
FieldByName(name string) (StructField, bool) 根據給定字符串返回字符串對應的結構體字段的信息,沒有找到時 bool 返回 false
FieldByIndex(index []int) StructField 多層成員訪問時,根據 []int 提供的每個結構體的字段索引,返回字段的信息,沒有找到時返回零值
FieldByNameFunc(match func(string) bool) (StructField,bool) 根據匹配函數匹配需要的字段

字段信息中含有:

type StructField struct {
    Name string          // 字段名
    PkgPath string       // 字段在結構體中的路徑
    Type      Type       // 字段的反射類型 reflect.Type
    Tag       StructTag  // 字段的結構體標簽
    Offset    uintptr    // 字段在結構體中的相對偏移
    Index     []int      // FieldByIndex中的索引順序
    Anonymous bool       // 是否為匿名字段
}

獲取字段的名稱與tag:

func reflectStructField()  {
	// 聲明一個空結構體
	type Cat struct {
		Name string
		// 帶有結構體tag的字段
		Type int `json:"type" id:"100"`
	}
	aCat := Cat{Name: "mimi", Type: 1}
	// 獲取結構體實例的反射類型對象
	typeOfCat := reflect.TypeOf(aCat)
	// 遍歷結構體所有成員
	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

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

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

通過反射獲取變量的值:

func reflectValue()  {
	var a int = 1024
	// 獲取變量a的反射值對象
	valueOfA := reflect.ValueOf(a)
	// 獲取interface{}類型的值, 通過類型斷言轉換
	var getA int = valueOfA.Interface().(int)
	// 獲取64位的值, 強制類型轉換為int類型
	var getA2 int = int(valueOfA.Int())
	fmt.Println(getA, getA2)
}
// 1024 1024

結構體

反射值對象(reflect.Value)提供對結構體訪問的方法,通過這些方法可以完成對結構體任意值的訪問:

方 法 備 注
Field(i int) Value 根據索引,返回索引對應的結構體成員字段的反射值對象
NumField() int 返回結構體成員字段數量
FieldByName(name string) Value 根據給定字符串返回字符串對應的結構體字段,沒有找到時返回零值
FieldByIndex(index []int) Value 多層成員訪問時,根據 []int 提供的每個結構體的字段索引,返回字段的值; 沒有找到時返回零值
FieldByNameFunc(match func(string) bool) Value 根據匹配函數匹配需要的字段,沒有找到時返回零值

空與有效性判斷

反射值對象(reflect.Value)提供一系列方法進行零值和空判定:

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

修改值

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

方法名 備 注
Elem() Value 取值指向的元素值(類似于*操作);對指針或接口時發生panic
Addr() Value 對可尋址的值返回其地址(類似于&操作);當值不可尋址時發生panic
CanAddr() bool 表示值是否可尋址
CanSet() bool 返回值能否被修改;要求值可尋址且是導出的字段

修改結構體字段的值(需要結構體地址,與導出字段):

func reflectModifyValue()  {
	type Dog struct {
		LegCount int
	}
	// 獲取dog實例地址的反射值對象
	valueOfDog := reflect.ValueOf(&Dog{})
	// 取出dog實例地址的元素
	valueOfDog = valueOfDog.Elem()
	vLegCount := valueOfDog.FieldByName("LegCount")
	vLegCount.SetInt(4)
	fmt.Println(vLegCount.Int())
}

函數調用

如果反射值對象(reflect.Value)為函數,可以通過Call()調用:參數使用反射值對象的切片[]reflect.Value構造后傳入,返回值通過[]reflect.Value返回。

func add(a, b int) int {
	return a + b
}
func reflectFunction() {
	// 將函數包裝為反射值對象
	funcValue := reflect.ValueOf(add)
	// 構造函數參數, 傳入兩個整型值
	paramList := []reflect.Value{reflect.ValueOf(10), reflect.ValueOf(20)}
	// 反射調用函數
	retList := funcValue.Call(paramList)
	// 獲取第一個返回值, 取整數值
	fmt.Println(retList[0].Int())
}

反射三定律

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

  • 反射可將interface類型變量轉換成反射對象;
  • 反射可將反射對象還原成interface對象;
  • 要修改反射對象,其值必須是可寫的(反射其指針類型);
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實現抽象的一個非常強大的工具;當向接口賦值時,接口會存儲實體的類型信息;反射就是通過接口的類型信息實現的。

interface類型是一種特殊類型,代表方法集合;可存放任何實現了其方法的值(實際存放的是(value,type)對)。reflect包中實現了反射的各種函數:

  • 提取interface的value的方法reflect.ValueOf()->reflect.Value;
  • 提取interface的type的方法reflect.TypeOf()->reflect.Type;

空interface類型(interface{})的方法集為空,所以可認為任何類型都實現了該接口;因此其可存放任何值。

底層結構

interface底層結構分為iface和eface描述接口,其區別是eface為不包含任何方法的空接口。

iface

iface定義如下:

  • tab 指向一個 itab 實體的指針:表示接口的類型(賦給此接口的實體類型);
  • data 指向接口具體的值:一般而言是一個指向堆內存的指針。
type iface struct {
	tab  *itab
	data unsafe.Pointer
}

itab結構:

  • _type 字段描述了實體的類型:包括內存對齊方式,大小等;
  • inter 字段則描述了接口的類型;
  • fun 字段放置是實體類中和接口方法對應(實體中其他方法不在此處)的方法地址,以實現接口調用方法的動態分派;一般在每次給接口賦值發生轉換時會更新此表。
type itab struct {
	inter  *interfacetype
	_type  *_type
	link   *itab
	hash   uint32
	bad    bool
	inhash bool
	unused [2]byte
	fun    [1]uintptr
}

iface結構全貌圖:

eface

eface結構:只維護了一個 _type 字段,表示空接口所承載的具體的實體類型。

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

原文鏈接:https://blog.csdn.net/alwaysrun/article/details/123024769

欄目分類
最近更新