Golang - 反射

反射调用方法

package main

import (
	"fmt"
	"reflect"
)

type stu struct {
	name  string
	score int
}

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

func (s stu) S1() int {
	return 100
}

// k 是为了模拟反射时传参
func (s stu) S2(k string) (string, int) {
	return k, s.score
}

func main() {
	s := stu{
		name:  "Tim",
		score: 99,
	}

	// Value..MethodByName 也只会找暴露给外部且值类型接收的方法
	// 就是只能找到 S1, S2 方法, 无法找到 SetScore 方法
	v1 := reflect.ValueOf(s).MethodByName("S1")

	// 如果方法存在则继续
	if v1.IsValid() {
		// 当方法没有参数时, 需要传 nil, 返回是个切片需要靠索引取值
		r := v1.Call(nil) // 传这个 []reflect.Value{} 也行
		fmt.Println(r[0])
	}

	v2 := reflect.ValueOf(s).MethodByName("S2")

	if v2.IsValid() {
		args := reflect.ValueOf("Tim")       // 用 ValueOf() 创建 Value, 且 "Tim" 是传给实际方法的参数
		r2 := v2.Call([]reflect.Value{args}) // Call() 的参数必须是 []Value, 所以上一步要造 Value 类型
		fmt.Println(r2[0], r2[1])            // 索引取值
	}
}

TypeOf

  • 根据变量名获取变量类型
package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 10

	t := reflect.TypeOf(a)

	fmt.Printf("%T, %[1]v\n", t)          // *reflect.rtype, int
	fmt.Printf("%T, %[1]v\n", t.String()) // string, int
}

方法

类型相关 - Kind(), Name()

  • 任何的数据类型: kind 都是基础类型名
  • 基础数据类型: Name 为空, Kind 为基础类型名
  • 自定义数据类型: Name 为 自定义数据类型名, Kind 为对应的基础数据类型名
  • 类型别名: Name 和 Kind 都是对应的基础数据类型名
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(), FieldByName(), FieldByIndex()

package main

import (
	"fmt"
	"reflect"
)

// Person 测试结构体
type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
}

// Stu 测试结构体
type Stu struct {
	Score int
	// 这种就是匿名引用, 如果是 Person Person, 就不是匿名引用, 第一个 Person 为命名, 第二个为指定的结构体类型
	Person
}

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

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

	// 取属性, 方式1, 根据索引
	sf1 := t.Field(1)
	// {Person  main.Person  8 [1] true}, 这个 true 说明 stu 结构体中的 person 结构体是匿名引用
	fmt.Println(sf1)

	// 取属性, 方式2, 根据 key
	sf2, ok := t.FieldByName("Person")
	if ok {
		fmt.Println(sf2)
	}

	// 根据索引取嵌套的属性
	sf3 := t.FieldByIndex([]int{1, 1})
	fmt.Println(sf3)

	// 上边三个返回值是 StructField 结构体, {Age  int json:"age" 16 [1] false}, 以这个举例
	fmt.Println(sf3.Name)  // Age, 结构体属性名
	fmt.Println(sf3.Type)  // int, 结构体属性数据类型
	fmt.Println(sf3.Tag)   // json:"age", 结构体属性 json tag
	fmt.Println(sf3.Index) // [1], 属性在当前结构体的索引
}

方法相关 - NumMethod(), Method(), MethodByName()

  • NumMethod() 这个方法只统计暴露给外部,并且是值接收者的方法
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)
	}
}

源码

TypeOf 实现

func TypeOf(i interface{}) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

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

  • 根据变量名获取值
package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 10

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

	// 这里输出的就是值
	fmt.Println(v)
}

方法

类型相关 - Kind()

  • Value 可以调用 Type(), 得到一个 Type
  • ValueType 都可以使用 Kind() 方法, 用法相同
package main

import (
	"fmt"
	"reflect"
)

func main() {
	var a int8
	a = 18

	// ValueOf(a) --> Value.Kind() --> Type
	v := reflect.ValueOf(a).Kind()
	fmt.Println(v.String())  // int8
}

修改值 - Elem()

package main

import (
	"fmt"
	"reflect"
)

func main() {
	a := 10
	
	// 拿到 Value
	e := reflect.ValueOf(&a).Elem()
	k := e.Kind()

	// 对应类型, 选择相应的修改方法进行修改
	switch k {	
	case reflect.Int:
		e.SetInt(100)
	default:
		fmt.Println("not string")
	}
	
	// 验证结果, 100
	fmt.Println(a)
}

检查

判断指针是否为空 - isNil()

  • 单独声明的指针, isNil 结果为 True
  • ValueOf() 的参数必须是指针
package main

import (
	"fmt"
	"reflect"
)

func main() {
	// 值类型
	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() - 判断是否存在

  • struct 中查找方法或属性是否存在
    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 是否存在
    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main() {
    	// map 中查找 key 是否存在
    	m := make(map[string]int, 5)
    
    	m["Tim"] = 100
    	m["Tom"] = 99
    
    	v2 := reflect.ValueOf(m)
    	valid := v2.MapIndex(reflect.ValueOf("Tim")).IsValid()
    	fmt.Println(valid)
    }
    

相关源码(部分)

// 返回值类型为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 {}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值