Golang - 反射


TypeOf - 拿到 Type

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int

	// 返回值是一個接口 Type
	t1 := reflect.TypeOf(a)

	// 可以直接打印, 輸出類型
	fmt.Println(t1)
}


Type 實現的方法

String()

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int

	// 拿到 Type
	t1 := reflect.TypeOf(a)

	// String(), 獲取類型
	r := t1.String()
	fmt.Println(r)
}


Kind() 和 Name() – 類型相關

package main

import (
	"fmt"
	"reflect"
)

type cat struct{}

type newInt int

type myNewInt = int

func main() {
	// 列出幾種不同的數據
	var a int      // 基礎 值類型
	var b []int    // 基礎 引用類型
	var c cat      // 自定義 值類型
	var d newInt   // 自定義類型
	var e myNewInt // 類型別名

	// 拿到一個 interface t, 可以直接打印, 輸出類型
	t1 := reflect.TypeOf(a)
	fmt.Println(t1)

	// String(), 獲取類型
	r := t1.String()
	fmt.Println(r)

	// Kind()
	// 1. 當數據爲基礎類型時, Kind 沒有變化
	ret := reflect.TypeOf(b)
	fmt.Printf("String(): %v, Kind(): %v, Name(): %v\n", ret.String(), ret.Kind(), ret.Name()) // String(): []int, Kind(): slice, Name():

	// 2. 當數據爲自定義類型時, Kind 爲其對應的基礎類型
	ret = reflect.TypeOf(c)
	fmt.Printf("String(): %v, Kind(): %v, Name(): %v\n", ret.String(), ret.Kind(), ret.Name()) // String(): main.cat, Kind(): struct, Name(): cat
	ret = reflect.TypeOf(d)
	fmt.Printf("String(): %v, Kind(): %v, Name(): %v\n", ret.String(), ret.Kind(), ret.Name()) // String(): main.newInt, Kind(): int, Name(): newInt

	// 3. 類型別名, 同對應的類型
	ret = reflect.TypeOf(e)
	fmt.Printf("String(): %v, Kind(): %v, Name(): %v\n", ret.String(), ret.Kind(), ret.Name()) // String(): int, Kind(): int, Name(): int

}


Field() – 屬性相關

package main

import (
	"fmt"
	"reflect"
)

// Person 測試結構體
type Person struct {
	Name string `json:"name"`
	Age  int
}

// Stu 測試結構體
type Stu struct {
	Score int
	Person
}

func main() {
	// 實例化
	stu1 := Stu{
		Score: 100,
		Person: Person{
			Name: "Tim",
			Age:  30,
		},
	}

	// 拿到 Type
	t := reflect.TypeOf(stu1)

	// 根據索引取屬性信息
	sf1 := t.Field(1)
	fmt.Println(sf1) // {Person  main.Person  8 [1] true}, 這個 true 說明 stu 結構體中的 person 結構體是匿名引用

	// 根據字符串取屬性信息
	sf2, ok := t.FieldByName("Score")
	if ok {
		fmt.Println(sf2)
	}

	// 根據索引去嵌套的屬性
	sf3 := t.FieldByIndex([]int{1, 0}) // 實際上是找到了 t[1][0], 也就是 stu1 的 Name 屬性
	fmt.Println(sf3)

	// 上邊三個返回值是 StructField 結構體
	// 取詳細的信息
	fmt.Println(sf3.Name)
	fmt.Println(sf3.Type)
	fmt.Println(sf3.Index)
	fmt.Println(sf3.Tag) // 如果結構體定義的時候, 聲明瞭 Tag, 就會返回, 反之則爲空
}

Method() – 方法相關

package main

import (
	"fmt"
	"reflect"
)

type stu struct {
	name  string
	age   int
	score int
}

func (s stu) Study() string {
	return "study"
}

func (s *stu) ChangeScore(newScore int) {
	s.score = newScore
}

func main() {
	stu1 := stu{
		name:  "Tim",
		age:   18,
		score: 100,
	}

	t := reflect.TypeOf(stu1)

	// 獲取方法數量, 這個方法只統計暴露給外部,並且是值接收者的方法
	numMethod := t.NumMethod()

	for i := 0; i < numMethod; i++ {
		// 根據索引
		fmt.Println(t.Method(i).Name)
	}

	// 根據字符串取方法
	m, ok := t.MethodByName("Study")
	if ok {
		fmt.Println(m.Name)
	}
}


源碼

Type接口

type Type interface {
	String() string                              // 直接打印就是這個
	
	Kind() Kind                                  // 返回種類
	Name() string                                // 返回類型
	
	Elem() Type                                  // 獲取指針對應的值
	
	NumField() int                               // 獲取屬性數量
	Field(i int) StructField                     // 根據索引獲取屬性
	FieldByIndex(index []int) StructField        // 根據索引獲取多個屬性
	FieldByName(name string) (StructField, bool) // 根據字符串獲取屬性
	
	NumMethod() int                              // 獲取方法數量
	Method(int) Method                           // 根據索引獲取方法
	MethodByName(string) (Method, bool)          // 根據字符串獲取方法
	
	FieldByNameFunc(match func(string) bool) (StructField, bool)
	Align() int
	FieldAlign() int
	PkgPath() string
	Size() uintptr
	Implements(u Type) bool
	AssignableTo(u Type) bool
	ConvertibleTo(u Type) bool
	Comparable() bool
	Bits() int
	ChanDir() ChanDir
	IsVariadic() bool
	In(i int) Type
	Key() Type
	Len() int
	NumIn() int
	NumOut() int
	Out(i int) Type
	common() *rtype
	uncommon() *uncommonType
}

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位複數類型
    String               // 字符串
    Func                 // 函數
    // 以下類型Name()返回爲空
    Array                // 數組
    Chan                 // 通道
    Interface            // 接口
    Map                  // 映射
    Ptr                  // 指針
    Slice                // 切片
    Struct               // 結構體
    UnsafePointer        // 底層指針
)


StructField類型源碼

// Field, FieldByIndex, FieldByName 返回值都是StructField

type StructField struct {
	// Name is the field name.
	Name string
	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}


Method結構體源碼

type Method struct {
	// Name is the method name.
	// PkgPath is the package path that qualifies a lower case (unexported)
	// method name. It is empty for upper case (exported) method names.
	// The combination of PkgPath and Name uniquely identifies a method
	// in a method set.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	Name    string
	PkgPath string

	Type  Type  // method type
	Func  Value // func with receiver as first argument
	Index int   // index for Type.Method
}


ValueOf - 拿到 Value

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 10

	// 拿到 Value
	v := reflect.ValueOf(a)

	// 這裏輸出的就是值
	fmt.Println(v)
}


Value 實現的方法

Kind() - 值的類型

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int8
	a = 18

	// 拿到 Value
	v := reflect.ValueOf(a)

	// 拿到值對應的類型
	t := v.Kind()

	switch t { // 可以寫到一起
	case reflect.String:
		fmt.Println("這是 string")
	case reflect.Int: // 注意, 這個 int 是區分 int8, int16, int32 的
		fmt.Println("這是 int")
	default:
		fmt.Println("不支持的類型")
	}
}


Elem() - 修改值

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int
	a = 18

	// 拿到 Value
	e := reflect.ValueOf(&a).Elem() // 修改值的情況下, ValueOf 的參數必須是指針

	// 拿到值對應的類型
	t := e.Kind()

	switch t { // 可以寫到一起
	case reflect.String:
		e.SetString("經過修改")
	case reflect.Int: // 注意, 這個 int 是區分 int8, int16, int32 的
		e.SetInt(123)
	default:
		fmt.Println("不支持的類型")
	}
	fmt.Println(a)
}


檢查

isNil() - 判斷指針

  • 常用於判斷指針是否爲空
  • 單獨聲明的指針, isNil 結果爲 True
package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 使用 isNil 判斷指針是否爲空, ValueOf 的參數必須是指針

	// 值類型
	var a1 int
	fmt.Println(reflect.ValueOf(&a1).IsNil()) // false

	var a2 *int
	fmt.Println(reflect.ValueOf(a2).IsNil()) // true

	var a3 = 100
	fmt.Println(reflect.ValueOf(&a3).IsNil()) // false

	// 引用類型
	var b1 []int
	fmt.Println(reflect.ValueOf(&b1).IsNil()) // false

	var b2 *[]int
	fmt.Println(reflect.ValueOf(b2).IsNil()) // true

	var b3 = []int{1, 2, 3}
	fmt.Println(reflect.ValueOf(&b3).IsNil()) // false
}


isValid() - 判斷是否存在

package main

import (
	"fmt"
	"reflect"
)

type stu struct {
	name  string
	score int
}

func (s *stu) SetScore(newScore int) {
	s.score = newScore
}

func (s stu) GetScore() int {
	return s.score
}

func main() {
	stu1 := stu{
		name:  "花花",
		score: 108,
	}

	v := reflect.ValueOf(stu1)

	// 查找字段是否存在
	fmt.Println(v.FieldByName("name").IsValid())

	// 查找方法是否存在
	fmt.Println(v.MethodByName("SetScore").IsValid()) // 同樣只查找暴露給外部的值類型方法

	// map 中查找 key 是否存在
	m := make(map[string]int, 5)

	m["Tim"] = 100
	m["Tom"] = 99

	v2 := reflect.ValueOf(m)
	fmt.Println(v2.MapIndex(reflect.ValueOf("Tim")).IsValid())
}


相關源碼(部分)

// 返回值類型爲Value
type Value struct {
	typ *rtype
	ptr unsafe.Pointer
	flag // uintptr
}

// 從指針取值
func (v Value) Elem() Value {}

// 修改值, 將數據以相應數據類型返回
func (v Value) Int() int64 {}
func (v Value) Float() float64 {}
func (v Value) Bool() bool {}
func (v Value) Uint() uint64 {}
func (v Value) Interface() interface{} {}
func (v Value) String() string {}
func (v Value) Bytes() []byte {}

// 檢查
func (v Value) IsNil() bool {}
func (v Value) IsValid() bool {}

// 屬性相關
func (v Value) Field(i int) Value {}
func (v Value) FieldByIndex(index []int) Value {}
func (v Value) FieldByName(name string) Value {}

// 方法相關
func (v Value) Method(i int) Value {}
func (v Value) NumMethod() int {}
func (v Value) MethodByName(name string) Value {}
func (v Value) Call(in []Value) []Value {}

// Map相關
func (v Value) MapIndex(key Value) Value {}
func (v Value) MapKeys() []Value {}

// 和TypeOf().Kind()相同, 使用哪個都行
func (v Value) Kind() Kind {}

反射調用方法

package main

import (
	"fmt"
	"reflect"
)

type stu struct {
	name  string
	score int
}

func (s *stu) SetScore(newScore int) {
	s.score = newScore
}

func (s stu) GetScore(k string) {
	fmt.Printf("%v %v score is %v\n", s.name, k, s.score)
}

func main() {
	stu1 := stu{
		name:  "花花",
		score: 108,
	}

	// 字符串是方法名, 注意: 只能找到值類型, 暴露給外部的方法
	v := reflect.ValueOf(stu1).MethodByName("GetScore")

	// 判斷方法是否存在
	if v.IsValid() {
		// 反射調用方法傳參, 參數類型必須是 reflect.ValueOf(args)
		args := reflect.ValueOf("math") // math 是實際的參數
		v.Call([]reflect.Value{args})   // 傳參的方法
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章