Golang學習 - reflect 包

------------------------------------------------------------

  在 reflect 包中,主要經過兩個函數 TypeOf() 和 ValueOf() 實現反射,TypeOf() 獲取到的結果是 reflect.Type 類型,ValueOf() 獲取到的結果是 reflect.Value 類型,這兩種類型都有不少方法能夠進一步獲取相關的反射信息。

  這裏有一個函數,能夠獲取指定對象的全部字段和方法:

------------------------------

// 獲取一個對象的字段和方法
package main

import (
	"fmt"
	"reflect"
)

// 獲取一個對象的字段和方法
func GetMembers(i interface{}) {
	// 獲取 i 的類型信息
	t := reflect.TypeOf(i)

	for {
		// 進一步獲取 i 的類別信息
		if t.Kind() == reflect.Struct {
			// 只有結構體能夠獲取其字段信息
			fmt.Printf("\n%-8v %v 個字段:\n", t, t.NumField())
			// 進一步獲取 i 的字段信息
			for i := 0; i < t.NumField(); i++ {
				fmt.Println(t.Field(i).Name)
			}
		}
		// 任何類型均可以獲取其方法信息
		fmt.Printf("\n%-8v %v 個方法:\n", t, t.NumMethod())
		// 進一步獲取 i 的方法信息
		for i := 0; i < t.NumMethod(); i++ {
			fmt.Println(t.Method(i).Name)
		}
		if t.Kind() == reflect.Ptr {
			// 若是是指針,則獲取其所指向的元素
			t = t.Elem()
		} else {
			// 不然上面已經處理過了,直接退出循環
			break
		}
	}
}

// 定義一個結構體用來進行測試
type sr struct {
	string
}

// 接收器爲實際類型
func (s sr) Read() {
}

// 接收器爲指針類型
func (s *sr) Write() {
}

func main() {
	// 測試
	GetMembers(&sr{})
}

/* 測試結果(能夠讀取私有字段):
*main.sr 2 個方法:
Read
Write

main.sr  1 個字段:
string

main.sr  1 個方法:
Read
*/

------------------------------

  咱們能夠經過下面的代碼獲取 reflect.Type 的全部方法,以便進行學習:

// reflect.Type 是一個接口類型
GetMembers(new(reflect.Type))

  列出的方法並不必定通用,須要根據不一樣的類型選擇使用,咱們在這裏給它們分一下類:

------------------------------

// 通用

// 獲取 t 類型的字符串描述,不要經過 String 來判斷兩種類型是否一致。
func (t *rtype) String() string

// 獲取 t 類型在其包中定義的名稱,未命名類型則返回空字符串。
func (t *rtype) Name() string

// 獲取 t 類型所在包的名稱,未命名類型則返回空字符串。
func (t *rtype) PkgPath() string

// 獲取 t 類型的類別。
func (t *rtype) Kind() reflect.Kind

// 獲取 t 類型的值在分配內存時的大小,功能和 unsafe.SizeOf 同樣。
func (t *rtype) Size() uintptr

// 獲取 t 類型的值在分配內存時的字節對齊值。
func (t *rtype) Align() int

// 獲取 t 類型的值做爲結構體字段時的字節對齊值。
func (t *rtype) FieldAlign() int

// 獲取 t 類型的方法數量。
func (t *rtype) NumMethod() int

// 根據索引獲取 t 類型的方法,若是方法不存在,則 panic。
// 若是 t 是一個實際的類型,則返回值的 Type 和 Func 字段會列出接收者。
// 若是 t 只是一個接口,則返回值的 Type 不列出接收者,Func 爲空值。
func (t *rtype) Method() reflect.Method

// 根據名稱獲取 t 類型的方法。
func (t *rtype) MethodByName(string) (reflect.Method, bool)

// 判斷 t 類型是否實現了 u 接口。
func (t *rtype) Implements(u reflect.Type) bool

// 判斷 t 類型的值能否轉換爲 u 類型。
func (t *rtype) ConvertibleTo(u reflect.Type) bool

// 判斷 t 類型的值能否賦值給 u 類型。
func (t *rtype) AssignableTo(u reflect.Type) bool

// 判斷 t 類型的值能否進行比較操做
func (t *rtype) Comparable() bool

------------------------------

// 示例
type inf interface {
	Method1()
	Method2()
}

type ss struct {
	a func()
}

func (i ss) Method1() {}
func (i ss) Method2() {}

func main() {
	s := reflect.TypeOf(ss{})
	i := reflect.TypeOf(new(inf)).Elem()

	Test(s)
	Test(i)
}

func Test(t reflect.Type) {
	if t.NumMethod() > 0 {
		fmt.Printf("\n--- %s ---\n", t)
		fmt.Println(t.Method(0).Type)
		fmt.Println(t.Method(0).Func.String())
	}
}

// 輸出結果:
// --- main.ss ---
// func(main.ss)
// <func(main.ss) Value>
//
// --- main.inf ---
// func()
// <invalid Value>

------------------------------

// 數值

// 獲取數值類型的位寬,t 必須是整型、浮點型、複數型
func (t *rtype) Bits() int

------------------------------

// 數組

// 獲取數組的元素個數
func (t *rtype) Len() int

------------------------------

// 映射

// 獲取映射的鍵類型
func (t *rtype) Key() reflect.Type

------------------------------

// 通道

// 獲取通道的方向
func (t *rtype) ChanDir() reflect.ChanDir

------------------------------

// 結構體

// 獲取字段數量
func (t *rtype) NumField() int

// 根據索引獲取字段
func (t *rtype) Field(int) reflect.StructField

// 根據名稱獲取字段
func (t *rtype) FieldByName(string) (reflect.StructField, bool)

// 根據指定的匹配函數 math 獲取字段
func (t *rtype) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool)

// 根據索引鏈獲取嵌套字段
func (t *rtype) FieldByIndex(index []int) reflect.StructField

------------------------------

// 函數

// 獲取函數的參數數量
func (t *rtype) NumIn() int

// 根據索引獲取函數的參數信息
func (t *rtype) In(int) reflect.Type

// 獲取函數的返回值數量
func (t *rtype) NumOut() int

// 根據索引獲取函數的返回值信息
func (t *rtype) Out(int) reflect.Type

// 判斷函數是否具備可變參數。
// 若是有可變參數,則 t.In(t.NumIn()-1) 將返回一個切片。
func (t *rtype) IsVariadic() bool

------------------------------

// 數組、切片、映射、通道、指針、接口

// 獲取元素類型、獲取指針所指對象類型,獲取接口的動態類型
func (t *rtype) Elem() reflect.Type

------------------------------

  下面的代碼用到了全部這些方法:

------------------------------

// 獲取各類類型的相關信息
package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

// 嵌套結構體
type ss struct {
	a struct {
		int
		string
	}
	int
	string
	bool
	float64
}

func (s ss) Method1(i int) string  { return "結構體方法1" }
func (s *ss) Method2(i int) string { return "結構體方法2" }

var (
	intValue   = int(0)
	int8Value  = int8(8)
	int16Value = int16(16)
	int32Value = int32(32)
	int64Value = int64(64)

	uIntValue   = uint(0)
	uInt8Value  = uint8(8)
	uInt16Value = uint16(16)
	uInt32Value = uint32(32)
	uInt64Value = uint64(64)

	byteValue    = byte(0)
	runeValue    = rune(0)
	uintptrValue = uintptr(0)

	boolValue   = false
	stringValue = ""

	float32Value = float32(32)
	float64Value = float64(64)

	complex64Value  = complex64(64)
	complex128Value = complex128(128)

	arrayValue  = [5]string{}           // 數組
	sliceValue  = []byte{0, 0, 0, 0, 0} // 切片
	mapValue    = map[string]int{}      // 映射
	chanValue   = make(chan int, 2)     // 通道
	structValue = ss{                   // 結構體
		struct {
			int
			string
		}{10, "子結構體"},
		20,
		"結構體",
		false,
		64.0,
	}

	func1Value = func(a, b, c int) string { // 函數(固定參數)
		return fmt.Sprintf("固定參數:%v %v %v", a, b, c)
	}

	func2Value = func(a, b int, c ...int) string { // 函數(動態參數)
		return fmt.Sprintf("動態參數:%v %v %v", a, b, c)
	}

	unsafePointer     = unsafe.Pointer(&structValue)    // 通用指針
	reflectType       = reflect.TypeOf(0)               // 反射類型
	reflectValue      = reflect.ValueOf(0)              // 反射值
	reflectArrayValue = reflect.ValueOf([]int{1, 2, 3}) // 切片反射值
	// 反射接口類型
	interfaceType = reflect.TypeOf(new(interface{})).Elem()
)

// 簡單類型
var simpleTypes = []interface{}{
	intValue, &intValue, // int
	int8Value, &int8Value, // int8
	int16Value, &int16Value, // int16
	int32Value, &int32Value, // int32
	int64Value, &int64Value, // int64
	uIntValue, &uIntValue, // uint
	uInt8Value, &uInt8Value, // uint8
	uInt16Value, &uInt16Value, // uint16
	uInt32Value, &uInt32Value, // uint32
	uInt64Value, &uInt64Value, // uint64
	byteValue, &byteValue, // byte
	runeValue, &runeValue, // rune
	uintptrValue, &uintptrValue, // uintptr
	boolValue, &boolValue, // bool
	stringValue, &stringValue, // string
	float32Value, &float32Value, // float32
	float64Value, &float64Value, // float64
	complex64Value, &complex64Value, // complex64
	complex128Value, &complex128Value, // complex128
}

// 複雜類型
var complexTypes = []interface{}{
	arrayValue, &arrayValue, // 數組
	sliceValue, &sliceValue, // 切片
	mapValue, &mapValue, // 映射
	chanValue, &chanValue, // 通道
	structValue, &structValue, // 結構體
	func1Value, &func1Value, // 定參函數
	func2Value, &func2Value, // 動參函數
	structValue.Method1, structValue.Method2, // 方法
	unsafePointer, &unsafePointer, // 指針
	reflectType, &reflectType, // 反射類型
	reflectValue, &reflectValue, // 反射值
	interfaceType, &interfaceType, // 接口反射類型
}

// 空值
var unsafeP unsafe.Pointer

// 空接口
var nilInterfece interface{}

func main() {
	// 測試簡單類型
	for i := 0; i < len(simpleTypes); i++ {
		PrintInfo(simpleTypes[i])
	}
	// 測試複雜類型
	for i := 0; i < len(complexTypes); i++ {
		PrintInfo(complexTypes[i])
	}
	// 測試單個對象
	PrintInfo(unsafeP)
	PrintInfo(&unsafeP)
	PrintInfo(nilInterfece)
	PrintInfo(&nilInterfece)
}

func PrintInfo(i interface{}) {
	if i == nil {
		fmt.Println("--------------------")
		fmt.Printf("無效接口值:%v\n", i)
		return
	}
	t := reflect.TypeOf(i)
	PrintType(t)
}

func PrintType(t reflect.Type) {
	fmt.Println("--------------------")
	// ----- 通用方法 -----
	fmt.Println("String             :", t.String())     // 類型字符串
	fmt.Println("Name               :", t.Name())       // 類型名稱
	fmt.Println("PkgPath            :", t.PkgPath())    // 所在包名稱
	fmt.Println("Kind               :", t.Kind())       // 所屬分類
	fmt.Println("Size               :", t.Size())       // 內存大小
	fmt.Println("Align              :", t.Align())      // 字節對齊
	fmt.Println("FieldAlign         :", t.FieldAlign()) // 字段對齊
	fmt.Println("NumMethod          :", t.NumMethod())  // 方法數量
	if t.NumMethod() > 0 {
		i := 0
		for ; i < t.NumMethod()-1; i++ {
			fmt.Println("    ┣", t.Method(i).Name) // 經過索引定位方法
		}
		fmt.Println("    ┗", t.Method(i).Name) // 經過索引定位方法
	}
	if sm, ok := t.MethodByName("String"); ok { // 經過名稱定位方法
		fmt.Println("MethodByName       :", sm.Index, sm.Name)
	}
	fmt.Println("Implements(i{})    :", t.Implements(interfaceType))  // 是否實現了指定接口
	fmt.Println("ConvertibleTo(int) :", t.ConvertibleTo(reflectType)) // 是否可轉換爲指定類型
	fmt.Println("AssignableTo(int)  :", t.AssignableTo(reflectType))  // 是否可賦值給指定類型的變量
	fmt.Println("Comparable         :", t.Comparable())               // 是否可進行比較操做
	// ----- 特殊類型 -----
	switch t.Kind() {
	// 指針型:
	case reflect.Ptr:
		fmt.Println("=== 指針型 ===")
		// 獲取指針所指對象
		t = t.Elem()
		fmt.Printf("轉換到指針所指對象 : %v\n", t.String())
		// 遞歸處理指針所指對象
		PrintType(t)
		return
	// 自由指針型:
	case reflect.UnsafePointer:
		fmt.Println("=== 自由指針 ===")
		// ...
	// 接口型:
	case reflect.Interface:
		fmt.Println("=== 接口型 ===")
		// ...
	}
	// ----- 簡單類型 -----
	// 數值型:
	if reflect.Int <= t.Kind() && t.Kind() <= reflect.Complex128 {
		fmt.Println("=== 數值型 ===")
		fmt.Println("Bits               :", t.Bits()) // 位寬
	}
	// ----- 複雜類型 -----
	switch t.Kind() {
	// 數組型:
	case reflect.Array:
		fmt.Println("=== 數組型 ===")
		fmt.Println("Len                :", t.Len())  // 數組長度
		fmt.Println("Elem               :", t.Elem()) // 數組元素類型
	// 切片型:
	case reflect.Slice:
		fmt.Println("=== 切片型 ===")
		fmt.Println("Elem               :", t.Elem()) // 切片元素類型
	// 映射型:
	case reflect.Map:
		fmt.Println("=== 映射型 ===")
		fmt.Println("Key                :", t.Key())  // 映射鍵
		fmt.Println("Elem               :", t.Elem()) // 映射值類型
	// 通道型:
	case reflect.Chan:
		fmt.Println("=== 通道型 ===")
		fmt.Println("ChanDir            :", t.ChanDir()) // 通道方向
		fmt.Println("Elem               :", t.Elem())    // 通道元素類型
	// 結構體:
	case reflect.Struct:
		fmt.Println("=== 結構體 ===")
		fmt.Println("NumField           :", t.NumField()) // 字段數量
		if t.NumField() > 0 {
			var i, j int
			// 遍歷結構體字段
			for i = 0; i < t.NumField()-1; i++ {
				field := t.Field(i) // 獲取結構體字段
				fmt.Printf("    ├ %v\n", field.Name)
				// 遍歷嵌套結構體字段
				if field.Type.Kind() == reflect.Struct && field.Type.NumField() > 0 {
					for j = 0; j < field.Type.NumField()-1; j++ {
						subfield := t.FieldByIndex([]int{i, j}) // 獲取嵌套結構體字段
						fmt.Printf("    │    ├ %v\n", subfield.Name)
					}
					subfield := t.FieldByIndex([]int{i, j}) // 獲取嵌套結構體字段
					fmt.Printf("    │    └ %%v\n", subfield.Name)
				}
			}
			field := t.Field(i) // 獲取結構體字段
			fmt.Printf("    └ %v\n", field.Name)
			// 經過名稱查找字段
			if field, ok := t.FieldByName("ptr"); ok {
				fmt.Println("FieldByName(ptr)   :", field.Name)
			}
			// 經過函數查找字段
			if field, ok := t.FieldByNameFunc(func(s string) bool { return len(s) > 3 }); ok {
				fmt.Println("FieldByNameFunc    :", field.Name)
			}
		}
	// 函數型:
	case reflect.Func:
		fmt.Println("=== 函數型 ===")
		fmt.Println("IsVariadic         :", t.IsVariadic()) // 是否具備變長參數
		fmt.Println("NumIn              :", t.NumIn())      // 參數數量
		if t.NumIn() > 0 {
			i := 0
			for ; i < t.NumIn()-1; i++ {
				fmt.Println("    ┣", t.In(i)) // 獲取參數類型
			}
			fmt.Println("    ┗", t.In(i)) // 獲取參數類型
		}
		fmt.Println("NumOut             :", t.NumOut()) // 返回值數量
		if t.NumOut() > 0 {
			i := 0
			for ; i < t.NumOut()-1; i++ {
				fmt.Println("    ┣", t.Out(i)) // 獲取返回值類型
			}
			fmt.Println("    ┗", t.Out(i)) // 獲取返回值類型
		}
	}
}

------------------------------------------------------------

  接下來學習 reflect.Value 的全部方法,仍是進行分類學習,這裏的不少操做(好比取地址、取切片、修改映射、通道進出、取值、賦值、函數調用等)和平時的操做都同樣,只不過在這裏須要用各類方法來操做,而平時只須要用一些符號來操做。

  注意:下面描述的 v 值是指 reflect.Value 所表明的實際值,而不是 reflect.Value 自己。

------------------------------

// 特殊

// 判斷 v 值是否可尋址
// 一、指針的 Elem() 可尋址
// 二、切片的元素可尋址
// 三、可尋址數組的元素可尋址
// 四、可尋址結構體的字段可尋址,方法不可尋址
// 也就是說,若是 v 值是指向數組的指針「&數組」,經過 v.Elem() 獲取該指針指向的數組,那麼
// 該數組就是可尋址的,同時該數組的元素也是可尋址的,若是 v 就是一個普通數組,不是經過解引
// 用獲得的數組,那麼該數組就不可尋址,其元素也不可尋址。結構體亦然。
func (v Value) CanAddr() bool

// 獲取 v 值的地址,至關於 & 取地址操做。v 值必須可尋址。
func (v Value) Addr() reflect.Value

// 判斷 v 值是否能夠被修改。只有可尋址的 v 值可被修改。
// 結構體中的非導出字段(經過 Field() 等方法獲取的)不能修改,全部方法不能修改。
func (v Value) CanSet() bool

// 判斷 v 值是否能夠轉換爲接口類型
// 結構體中的非導出字段(經過 Field() 等方法獲取的)不能轉換爲接口類型
func (v Value) CanInterface() bool

// 將 v 值轉換爲空接口類型。v 值必須可轉換爲接口類型。
func (v Value) Interface() interface{}

// 使用一對 uintptr 返回接口的數據
func (v Value) InterfaceData() [2]uintptr

------------------------------

// 示例:
type ss struct {
	A int
	a int
}

func (s ss) Method1(i int) string  { return "結構體方法1" }
func (s *ss) Method2(i int) string { return "結構體方法2" }

func main() {
	v1 := reflect.ValueOf(ss{})                   // 結構體
	v2 := reflect.ValueOf(&ss{})                  // 結構體指針
	v3 := reflect.ValueOf(&ss{}).Elem()           // 可尋址結構體
	v4 := reflect.ValueOf(&ss{}).Elem().Field(0)  // 可尋址結構體的共有字段
	v5 := reflect.ValueOf(&ss{}).Elem().Field(1)  // 可尋址結構體的私有字段
	v6 := reflect.ValueOf(&ss{}).Method(0)        // 結構體指針的方法
	v7 := reflect.ValueOf(&ss{}).Elem().Method(0) // 結構體的方法

	fmt.Println(v1.CanAddr()) // false
	fmt.Println(v2.CanAddr()) // false
	fmt.Println(v3.CanAddr()) // true
	fmt.Println(v4.CanAddr()) // true
	fmt.Println(v5.CanAddr()) // true
	fmt.Println(v6.CanAddr()) // false
	fmt.Println(v7.CanAddr()) // false
	fmt.Println("----------")
	fmt.Println(v1.CanSet()) // false
	fmt.Println(v2.CanSet()) // false
	fmt.Println(v3.CanSet()) // true
	fmt.Println(v4.CanSet()) // true
	fmt.Println(v5.CanSet()) // false
	fmt.Println(v6.CanSet()) // false
	fmt.Println(v6.CanSet()) // false
	fmt.Println("----------")
	fmt.Println(v1.CanInterface()) // true
	fmt.Println(v2.CanInterface()) // true
	fmt.Println(v3.CanInterface()) // true
	fmt.Println(v4.CanInterface()) // true
	fmt.Println(v5.CanInterface()) // false
	fmt.Println(v6.CanInterface()) // true
	fmt.Println(v7.CanInterface()) // true
}

------------------------------

// 指針

// 將 v 值轉換爲 uintptr 類型,v 值必須是切片、映射、通道、函數、指針、自由指針。
func (v Value) Pointer() uintptr

// 獲取 v 值的地址。v 值必須是可尋址類型(CanAddr)。
func (v Value) UnsafeAddr() uintptr

// 將 UnsafePointer 類別的 v 值修改成 x,v 值必須是 UnsafePointer 類別,必須可修改。
func (v Value) SetPointer(x unsafe.Pointer)

// 判斷 v 值是否爲 nil,v 值必須是切片、映射、通道、函數、接口、指針。
// IsNil 並不總等價於 Go 的潛在比較規則,好比對於 var i interface{},i == nil 將返回
// true,可是 reflect.ValueOf(i).IsNil() 將 panic。
func (v Value) IsNil() bool

// 獲取「指針所指的對象」或「接口所包含的對象」
func (v Value) Elem() reflect.Value

------------------------------

// 接口

// 獲取「指針所指的對象」或「接口所包含的對象」
func (v Value) Elem() reflect.Value

------------------------------

// 通用

// 獲取 v 值的字符串描述
func (v Value) String() string

// 獲取 v 值的類型
func (v Value) Type() reflect.Type

// 返回 v 值的類別,若是 v 是空值,則返回 reflect.Invalid。
func (v Value) Kind() reflect.Kind

// 獲取 v 的方法數量
func (v Value) NumMethod() int

// 根據索引獲取 v 值的方法,方法必須存在,不然 panic
// 使用 Call 調用方法的時候不用傳入接收者,Go 會自動把 v 做爲接收者傳入。
func (v Value) Method(int) reflect.Value

// 根據名稱獲取 v 值的方法,若是該方法不存在,則返回空值(reflect.Invalid)。
func (v Value) MethodByName(string) reflect.Value

// 判斷 v 自己(不是 v 值)是否爲零值。
// 若是 v 自己是零值,則除了 String 以外的其它全部方法都會 panic。
func (v Value) IsValid() bool

// 將 v 值轉換爲 t 類型,v 值必須可轉換爲 t 類型,不然 panic。
func (v Value) Convert(t Type) reflect.Value

------------------------------

// 示例
func main() {
	var v reflect.Value      // 未包含任何數據
	fmt.Println(v.IsValid()) // false

	var i *int
	v = reflect.ValueOf(i)   // 包含一個指針
	fmt.Println(v.IsValid()) // true

	v = reflect.ValueOf(nil) // 包含一個 nil 指針
	fmt.Println(v.IsValid()) // false

	v = reflect.ValueOf(0)   // 包含一個 int 數據
	fmt.Println(v.IsValid()) // true
}

------------------------------

// 獲取

// 獲取 v 值的內容,若是 v 值不是有符號整型,則 panic。
func (v Value) Int() int64

// 獲取 v 值的內容,若是 v 值不是無符號整型(包括 uintptr),則 panic。
func (v Value) Uint() uint64

// 獲取 v 值的內容,若是 v 值不是浮點型,則 panic。
func (v Value) Float() float64

// 獲取 v 值的內容,若是 v 值不是複數型,則 panic。
func (v Value) Complex() complex128

// 獲取 v 值的內容,若是 v 值不是布爾型,則 panic。
func (v Value) Bool() bool

// 獲取 v 值的長度,v 值必須是字符串、數組、切片、映射、通道。
func (v Value) Len() int

// 獲取 v 值的容量,v 值必須是數值、切片、通道。
func (v Value) Cap() int

// 獲取 v 值的第 i 個元素,v 值必須是字符串、數組、切片,i 不能超出範圍。
func (v Value) Index(i int) reflect.Value

// 獲取 v 值的內容,若是 v 值不是字節切片,則 panic。
func (v Value) Bytes() []byte

// 獲取 v 值的切片,切片長度 = j - i,切片容量 = v.Cap() - i。
// v 必須是字符串、數值、切片,若是是數組則必須可尋址。i 不能超出範圍。
func (v Value) Slice(i, j int) reflect.Value

// 獲取 v 值的切片,切片長度 = j - i,切片容量 = k - i。
// i、j、k 不能超出 v 的容量。i <= j <= k。
// v 必須是字符串、數值、切片,若是是數組則必須可尋址。i 不能超出範圍。
func (v Value) Slice3(i, j, k int) reflect.Value

// 根據 key 鍵獲取 v 值的內容,v 值必須是映射。
// 若是指定的元素不存在,或 v 值是未初始化的映射,則返回零值(reflect.ValueOf(nil))
func (v Value) MapIndex(key Value) reflect.Value

// 獲取 v 值的全部鍵的無序列表,v 值必須是映射。
// 若是 v 值是未初始化的映射,則返回空列表。
func (v Value) MapKeys() []reflect.Value

// 判斷 x 是否超出 v 值的取值範圍,v 值必須是有符號整型。
func (v Value) OverflowInt(x int64) bool

// 判斷 x 是否超出 v 值的取值範圍,v 值必須是無符號整型。
func (v Value) OverflowUint(x uint64) bool

// 判斷 x 是否超出 v 值的取值範圍,v 值必須是浮點型。
func (v Value) OverflowFloat(x float64) bool

// 判斷 x 是否超出 v 值的取值範圍,v 值必須是複數型。
func (v Value) OverflowComplex(x complex128) bool

------------------------------

// 設置(這些方法要求 v 值必須可修改)

// 設置 v 值的內容,v 值必須是有符號整型。
func (v Value) SetInt(x int64)

// 設置 v 值的內容,v 值必須是無符號整型。
func (v Value) SetUint(x uint64)

// 設置 v 值的內容,v 值必須是浮點型。
func (v Value) SetFloat(x float64)

// 設置 v 值的內容,v 值必須是複數型。
func (v Value) SetComplex(x complex128)

// 設置 v 值的內容,v 值必須是布爾型。
func (v Value) SetBool(x bool)

// 設置 v 值的內容,v 值必須是字符串。
func (v Value) SetString(x string)

// 設置 v 值的長度,v 值必須是切片,n 不能超出範圍,不能爲負數。
func (v Value) SetLen(n int)

// 設置 v 值的內容,v 值必須是切片,n 不能超出範圍,不能小於 Len。
func (v Value) SetCap(n int)

// 設置 v 值的內容,v 值必須是字節切片。x 能夠超出 v 值容量。
func (v Value) SetBytes(x []byte)

// 設置 v 值的鍵和值,若是鍵存在,則修改其值,若是鍵不存在,則添加鍵和值。
// 若是將 val 設置爲零值(reflect.ValueOf(nil)),則刪除該鍵。
// 若是 v 值是一個未初始化的 map,則 panic。
func (v Value) SetMapIndex(key, val reflect.Value)

// 設置 v 值的內容,v 值必須可修改,x 必須能夠賦值給 v 值。
func (v Value) Set(x reflect.Value)

------------------------------

// 結構體

// 獲取 v 值的字段數量,v 值必須是結構體。
func (v Value) NumField() int

// 根據索引獲取 v 值的字段,v 值必須是結構體。若是字段不存在則 panic。
func (v Value) Field(i int) reflect.Value

// 根據索引鏈獲取 v 值的嵌套字段,v 值必須是結構體。
func (v Value) FieldByIndex(index []int) reflect.Value

// 根據名稱獲取 v 值的字段,v 值必須是結構體。
// 若是指定的字段不存在,則返回零值(reflect.ValueOf(nil))
func (v Value) FieldByName(string) reflect.Value

// 根據匹配函數 match 獲取 v 值的字段,v 值必須是結構體。
// 若是沒有匹配的字段,則返回零值(reflect.ValueOf(nil))
func (v Value) FieldByNameFunc(match func(string) bool) Value

------------------------------

// 示例
type ss struct {
	s struct {
		B int
		b int
	}
	A int
	a int
}

func main() {
	var v = reflect.ValueOf(ss{})
	for i := 0; i < v.NumField(); i++ {
		field := v.Field(i)
		fmt.Println("字段:", field.Type().String())
		if field.Type().Kind() == reflect.Struct {
			for j := 0; j < field.NumField(); j++ {
				subfield := field.Field(j)
				fmt.Println("    嵌套字段:", subfield.Type().String())
			}
		}
	}
}

// 輸出結果:
// 字段: struct { B int; b int }
//     嵌套字段: int
//     嵌套字段: int
// 字段: int
// 字段: int

------------------------------

// 通道

// 發送數據(會阻塞),v 值必須是可寫通道。
func (v Value) Send(x reflect.Value)

// 接收數據(會阻塞),v 值必須是可讀通道。
func (v Value) Recv() (x reflect.Value, ok bool)

// 嘗試發送數據(不會阻塞),v 值必須是可寫通道。
func (v Value) TrySend(x reflect.Value) bool

// 嘗試接收數據(不會阻塞),v 值必須是可讀通道。
func (v Value) TryRecv() (x reflect.Value, ok bool)

// 關閉通道,v 值必須是通道。
func (v Value) Close()

------------------------------

// 示例
func main() {
	ch := make(chan int, 2)
	v := reflect.ValueOf(ch)

	a := reflect.ValueOf(1)
	b := reflect.ValueOf(2)

	v.Send(a)
	if ok := v.TrySend(b); ok {
		fmt.Println("嘗試發送成功!") // 嘗試發送成功!
	}
	if i, ok := v.Recv(); ok {
		fmt.Println("接收成功:", i) // 接收成功: 1
	}
	if i, ok := v.TryRecv(); ok {
		fmt.Println("嘗試接收成功:", i) // 嘗試接收成功: 2
	}
}

------------------------------

// 函數

// 經過參數列表 in 調用 v 值所表明的函數(或方法)。函數的返回值存入 r 中返回。
// 要傳入多少參數就在 in 中存入多少元素。
// Call 便可以調用定參函數(參數數量固定),也能夠調用變參函數(參數數量可變)。
func (v Value) Call(in []Value) (r []Value)

// 經過參數列表 in 調用 v 值所表明的函數(或方法)。函數的返回值存入 r 中返回。
// 函數指定了多少參數就在 in 中存入多少元素,變參做爲一個單獨的參數提供。
// CallSlice 只能調用變參函數。
func (v Value) CallSlice(in []Value) []Value

------------------------------

// 示例
var f1 = func(a int, b []int) { fmt.Println(a, b) }
var f2 = func(a int, b ...int) { fmt.Println(a, b) }

func main() {
	v1 := reflect.ValueOf(f1)
	v2 := reflect.ValueOf(f2)

	a := reflect.ValueOf(1)
	b := reflect.ValueOf([]int{1, 2, 3})

	v1.Call([]reflect.Value{a, b})
	v2.Call([]reflect.Value{a, a, a, a, a, a})

	//v1.CallSlice([]reflect.Value{a, b}) // 非變參函數,不能用 CallSlice。
	v2.CallSlice([]reflect.Value{a, b})
}

------------------------------

  下面的代碼用到了全部這些方法:

------------------------------

// 獲取各類值的相關信息
package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

// 嵌套結構體
type ss struct {
	a struct {
		int
		string
	}
	int
	string
	bool
	float64
}

func (s ss) Method1(i int) string  { return "結構體方法1" }
func (s *ss) Method2(i int) string { return "結構體方法2" }

var (
	intValue   = int(0)
	int8Value  = int8(8)
	int16Value = int16(16)
	int32Value = int32(32)
	int64Value = int64(64)

	uIntValue   = uint(0)
	uInt8Value  = uint8(8)
	uInt16Value = uint16(16)
	uInt32Value = uint32(32)
	uInt64Value = uint64(64)

	byteValue    = byte(0)
	runeValue    = rune(0)
	uintptrValue = uintptr(0)

	boolValue   = false
	stringValue = "stringValue"

	float32Value = float32(32)
	float64Value = float64(64)

	complex64Value  = complex64(64)
	complex128Value = complex128(128)

	arrayValue  = [5]string{}           // 數組
	sliceValue  = []byte{0, 0, 0, 0, 0} // 切片
	mapValue    = map[string]int{}      // 映射
	chanValue   = make(chan int, 2)     // 通道
	structValue = ss{                   // 結構體
		struct {
			int
			string
		}{10, "子結構體"},
		20,
		"結構體",
		false,
		64.0,
	}

	func1Value = func(i int) string { // 函數(固定參數)
		return fmt.Sprintf("固定參數:%v", i)
	}

	func2Value = func(i ...int) string { // 函數(動態參數)
		return fmt.Sprintf("動態參數:%v", i)
	}

	unsafePointer     = unsafe.Pointer(&structValue)    // 通用指針
	reflectType       = reflect.TypeOf(0)               // 反射類型
	reflectValue      = reflect.ValueOf(0)              // 反射值
	reflectArrayValue = reflect.ValueOf([]int{1, 2, 3}) // 切片反射值
	// 反射接口類型
	interfaceType = reflect.TypeOf(new(interface{})).Elem()
)

// 簡單類型
var simpleTypes = []interface{}{
	intValue, &intValue, // int
	int8Value, &int8Value, // int8
	int16Value, &int16Value, // int16
	int32Value, &int32Value, // int32
	int64Value, &int64Value, // int64
	uIntValue, &uIntValue, // uint
	uInt8Value, &uInt8Value, // uint8
	uInt16Value, &uInt16Value, // uint16
	uInt32Value, &uInt32Value, // uint32
	uInt64Value, &uInt64Value, // uint64
	byteValue, &byteValue, // byte
	runeValue, &runeValue, // rune
	uintptrValue, &uintptrValue, // uintptr
	boolValue, &boolValue, // bool
	stringValue, &stringValue, // string
	float32Value, &float32Value, // float32
	float64Value, &float64Value, // float64
	complex64Value, &complex64Value, // complex64
	complex128Value, &complex128Value, // complex128
}

// 複雜類型
var complexTypes = []interface{}{
	arrayValue, &arrayValue, // 數組
	sliceValue, &sliceValue, // 切片
	mapValue, &mapValue, // 映射
	chanValue, &chanValue, // 通道
	structValue, &structValue, // 結構體
	func1Value, &func1Value, // 定參函數
	func2Value, &func2Value, // 動參函數
	structValue.Method1, structValue.Method2, // 方法
	unsafePointer, &unsafePointer, // 指針
	reflectType, &reflectType, // 反射類型
	reflectValue, &reflectValue, // 反射值
	interfaceType, &interfaceType, // 接口反射類型
}

// 空值
var unsafeP unsafe.Pointer

// 空接口
var nilInterfece interface{}

func main() {
	// 測試簡單類型
	for i := 0; i < len(simpleTypes); i++ {
		PrintInfo(simpleTypes[i])
	}
	// 測試複雜類型
	for i := 0; i < len(complexTypes); i++ {
		PrintInfo(complexTypes[i])
	}
	// 測試單個對象
	PrintInfo(&unsafeP)
	PrintInfo(nilInterfece)
	// PrintInfo(&nilInterfece) // 會引起 panic
}

func PrintInfo(i interface{}) {
	if i == nil {
		fmt.Println("--------------------")
		fmt.Printf("無效接口值:%v\n", i)
		fmt.Println("--------------------")
		return
	}
	v := reflect.ValueOf(i)
	PrintValue(v)
}

func PrintValue(v reflect.Value) {
	fmt.Println("--------------------")
	// ----- 通用方法 -----
	fmt.Println("String             :", v.String())  // 反射值的字符串形式
	fmt.Println("Type               :", v.Type())    // 反射值的類型
	fmt.Println("Kind               :", v.Kind())    // 反射值的類別
	fmt.Println("CanAddr            :", v.CanAddr()) // 是否能夠獲取地址
	fmt.Println("CanSet             :", v.CanSet())  // 是否能夠修改
	if v.CanAddr() {
		fmt.Println("Addr               :", v.Addr())       // 獲取地址
		fmt.Println("UnsafeAddr         :", v.UnsafeAddr()) // 獲取自由地址
	}
	// 是否可轉換爲接口對象
	fmt.Println("CanInterface       :", v.CanInterface())
	if v.CanInterface() {
		fmt.Println("Interface          :", v.Interface()) // 轉換爲接口對象
	}
	// 獲取方法數量
	fmt.Println("NumMethod          :", v.NumMethod())
	if v.NumMethod() > 0 {
		// 遍歷方法
		i := 0
		for ; i < v.NumMethod()-1; i++ {
			fmt.Printf("    ┣ %v\n", v.Method(i).String())
			//			if i >= 4 { // 只列舉 5 個
			//				fmt.Println("    ┗ ...")
			//				break
			//			}
		}
		fmt.Printf("    ┗ %v\n", v.Method(i).String())
		// 經過名稱獲取方法
		fmt.Println("MethodByName       :", v.MethodByName("String").String())
	}
	// ----- 可獲取指針的類型 -----
	switch v.Kind() {
	case reflect.Slice, reflect.Map, reflect.Chan, reflect.Func,
		reflect.Ptr, reflect.UnsafePointer:
		fmt.Println("Pointer            :", v.Pointer())
	}
	// ----- 特殊類型 -----
	switch v.Kind() {
	// 指針:
	case reflect.Ptr:
		fmt.Println("=== 指針 ===")
		// 獲取指針地址
		if !v.IsNil() {
			// 獲取指針所指對象
			v = v.Elem() // 只有指針和接口類型可使用 Elem()
			fmt.Printf("轉換到指針所指對象 : %v\n", v.Type())
			// 遞歸處理指針所指對象
			PrintValue(v)
			return
		}
	// 自由指針:
	case reflect.UnsafePointer:
		fmt.Println("=== 自由指針 ===")
		if v.Pointer() == 0 {
			v.SetPointer(unsafePointer)
			fmt.Println("從新指向新對象     :", v.Pointer())
		}
		// 將自由指針轉換爲 *ss 指針(由於定義 unsafePointer 時已經肯定了類型)
		s := (*ss)(v.Interface().(unsafe.Pointer))
		// 獲取反射值
		v = reflect.ValueOf(s)
		if !v.IsNil() {
			// 獲取指針所指對象
			v = v.Elem() // 只有指針和接口類型可使用 Elem()
			fmt.Printf("轉換到指針所指對象 : %v\n", v.Type())
			// 遞歸處理指針所指對象
			PrintValue(v)
			return
		}
	// 接口:
	case reflect.Interface:
		fmt.Println("=== 接口 ===")
		// 獲取接口數據
		fmt.Println("InterfaceData      :", v.InterfaceData())
		// 獲取接口所包含的對象
		v = v.Elem() // 只有指針和接口類型可使用 Elem()
		fmt.Printf("轉換到接口所含對象 : %v\n", v.Type())
		// 遞歸處理接口的動態對象
		PrintValue(v)
		return
	}
	// ----- 簡單類型 -----
	// 有符號整型:
	if reflect.Int <= v.Kind() && v.Kind() <= reflect.Int64 {
		fmt.Println("=== 有符號整型 ===")
		fmt.Println("Int                :", v.Int()) // 獲取值
		if v.CanSet() {
			v.SetInt(10)                                 // 設置值
			fmt.Println("Int                :", v.Int()) // 獲取值
			v.Set(reflect.ValueOf(20).Convert(v.Type())) // 設置值
		}
		fmt.Println("Int                :", v.Int())           // 獲取值
		fmt.Println("OverflowInt        :", v.OverflowInt(10)) // 是否溢出
	}
	// 無符號整型:
	if reflect.Uint <= v.Kind() && v.Kind() <= reflect.Uint64 {
		fmt.Println("=== 無符號整型 ===")
		fmt.Println("Uint               :", v.Uint()) // 獲取值
		if v.CanSet() {
			v.SetUint(10)                                 // 設置值
			fmt.Println("Uint               :", v.Uint()) // 獲取值
			v.Set(reflect.ValueOf(20).Convert(v.Type()))  // 設置值
		}
		fmt.Println("Uint               :", v.Uint())           // 獲取值
		fmt.Println("OverflowUint       :", v.OverflowUint(10)) // 是否溢出
	}
	switch v.Kind() {
	// 浮點數:
	case reflect.Float32, reflect.Float64:
		fmt.Println("=== 浮點數 ===")
		fmt.Println("Float              :", v.Float()) // 獲取值
		if v.CanSet() {
			v.SetFloat(10)                                 // 設置值
			fmt.Println("Float              :", v.Float()) // 獲取值
			v.Set(reflect.ValueOf(20).Convert(v.Type()))   // 設置值
		}
		fmt.Println("Float              :", v.Float())           // 獲取值
		fmt.Println("OverflowFloat      :", v.OverflowFloat(10)) // 是否溢出
	// 複數:
	case reflect.Complex64, reflect.Complex128:
		fmt.Println("=== 複數 ===")
		fmt.Println("Complex            :", v.Complex()) // 獲取值
		if v.CanSet() {
			v.SetComplex(10)                                   // 設置值
			fmt.Println("Complex            :", v.Complex())   // 獲取值
			v.Set(reflect.ValueOf(20 + 20i).Convert(v.Type())) // 設置值
		}
		fmt.Println("Complex            :", v.Complex())           // 獲取值
		fmt.Println("OverflowComplex    :", v.OverflowComplex(10)) // 是否溢出
	// 布爾型:
	case reflect.Bool:
		fmt.Println("=== 布爾型 ===")
		fmt.Println("Bool               :", v.Bool()) // 獲取值
		if v.CanSet() {
			v.SetBool(true)                               // 設置值
			fmt.Println("Bool               :", v.Bool()) // 獲取值
			v.Set(reflect.ValueOf(false))                 // 設置值
		}
		fmt.Println("Bool               :", v.Bool()) // 獲取值
	// 字符串:
	case reflect.String:
		fmt.Println("=== 字符串 ===")
		fmt.Println("String             :", v.String()) // 獲取值
		if v.CanSet() {
			v.SetString("abc")                              // 設置值
			fmt.Println("String             :", v.String()) // 獲取值
			v.Set(reflect.ValueOf("def"))                   // 設置值
		}
		fmt.Println("String             :", v.String()) // 獲取值
	// ----- 複雜類型 -----
	// 切片型:
	case reflect.Slice:
		fmt.Println("=== 切片型 ===")
		fmt.Println("Len                :", v.Len()) // 獲取長度
		fmt.Println("Cap                :", v.Cap()) // 獲取容量
		if v.CanSet() {
			v.SetLen(4) // 不能大於 cap
			v.SetCap(4) // 不能小於 len,只能縮,不能擴
			fmt.Println("SetLen, SetCap     :", v.Len(), v.Cap())
			// 從新指定字節內容
			if v.Type().Elem().Kind() == reflect.Uint8 {
				v.SetBytes([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
			}
			fmt.Println("SetByte            :", []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
		}
		// 獲取字節內容
		if v.Type().Elem().Kind() == reflect.Uint8 {
			fmt.Println("Bytes              :", v.Bytes())
		}
		// 根據索引獲取元素
		if v.Len() > 0 {
			for i := 0; i < v.Len(); i++ {
				fmt.Println("Index              :", v.Index(i))
			}
		}
		// 獲取一個指定範圍的切片
		// 參數:起始下標,結束下標
		// 長度 = 結束下標 - 起始下標
		s1 := v.Slice(1, 2)
		fmt.Println("Slice              :", s1)
		fmt.Println("Len                :", s1.Len()) // 獲取長度
		fmt.Println("Cap                :", s1.Cap()) // 獲取容量
		// 獲取一個指定範圍和容量的切片
		// 參數:起始下標,結束下標,容量下標
		// 長度 = 結束下標 - 起始下標
		// 容量 = 容量下標 - 起始下標
		s2 := v.Slice3(1, 2, 4)
		fmt.Println("Slice              :", s2)
		fmt.Println("Len                :", s2.Len()) // 獲取長度
		fmt.Println("Cap                :", s2.Cap()) // 獲取容量
	// 映射型:
	case reflect.Map:
		fmt.Println("=== 映射型 ===")
		// 設置鍵值,不須要檢測 CanSet
		v.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
		v.SetMapIndex(reflect.ValueOf("b"), reflect.ValueOf(2))
		v.SetMapIndex(reflect.ValueOf("c"), reflect.ValueOf(3))
		// 獲取鍵列表
		fmt.Println("MapKeys            :", v.MapKeys())
		for _, idx := range v.MapKeys() {
			// 根據鍵獲取值
			fmt.Println("MapIndex           :", v.MapIndex(idx))
		}
	// 結構體:
	case reflect.Struct:
		fmt.Println("=== 結構體 ===")
		// 獲取字段個數
		fmt.Println("NumField           :", v.NumField())
		if v.NumField() > 0 {
			var i, j int
			// 遍歷結構體字段
			for i = 0; i < v.NumField()-1; i++ {
				field := v.Field(i) // 獲取結構體字段
				fmt.Printf("    ├ %-8v %v\n", field.Type(), field.String())
				// 遍歷嵌套結構體字段
				if field.Kind() == reflect.Struct && field.NumField() > 0 {
					for j = 0; j < field.NumField()-1; j++ {
						subfield := v.FieldByIndex([]int{i, j}) // 獲取嵌套結構體字段
						fmt.Printf("    │    ├ %-8v %v\n", subfield.Type(), subfield.String())
						// if i >= 4 { // 只列舉 5 個
						// 	fmt.Println("        ┗ ...")
						// 	break
						// }
					}
					subfield := v.FieldByIndex([]int{i, j}) // 獲取嵌套結構體字段
					fmt.Printf("    │    └ %-8v %v\n", subfield.Type(), subfield.String())
				}
				// if i >= 4 { // 只列舉 5 個
				// 	fmt.Println("    ┗ ...")
				// 	break
				// }
			}
			field := v.Field(i) // 獲取結構體字段
			fmt.Printf("    └ %-8v %v\n", field.Type(), field.String())
			// 經過名稱查找字段
			if v := v.FieldByName("ptr"); v.IsValid() {
				fmt.Println("FieldByName(ptr)   :", v.Type().Name())
			}
			// 經過函數查找字段
			v := v.FieldByNameFunc(func(s string) bool { return len(s) > 3 })
			if v.IsValid() {
				fmt.Println("FieldByNameFunc    :", v.Type().Name())
			}
		}
	// 通道型:
	case reflect.Chan:
		fmt.Println("=== 通道型 ===")
		// 發送數據(會阻塞)
		v.Send(reflectValue)
		// 嘗試發送數據(不會阻塞)
		fmt.Println("TrySend            :", v.TrySend(reflectValue))
		// 接收數據(會阻塞)
		if x, ok := v.Recv(); ok {
			fmt.Println("Recv               :", x) //
		}
		// 嘗試接收數據(不會阻塞)
		if x, ok := v.TryRecv(); ok {
			fmt.Println("TryRecv            :", x) //
		}
		// 由於要執行兩次,通道和通道指針各執行一次,關閉後第二次就沒法執行了。
		// v.Close()
	// 函數型:
	case reflect.Func:
		fmt.Println("=== 函數型 ===")
		// 判斷函數是否具備變長參數
		if v.Type().IsVariadic() {
			// 與可變參數對應的實參必須是切片類型的反射值(reflectArrayValue)。
			fmt.Println("CallSlice          :", v.CallSlice([]reflect.Value{reflectArrayValue})) //
			// 也能夠用 v.Call 調用變長參數的函數,只需傳入 reflectValue 便可。
		} else {
			// 根據函數定義的參數數量,傳入相應數量的反射值(reflectValue)。
			fmt.Println("Call               :", v.Call([]reflect.Value{reflectValue})) //
		}
	}
}

------------------------------------------------------------



相關文章
相關標籤/搜索