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})   // 传参的方法
	}
}

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