GoLang反射

x33g5p2x  于2022-08-17 转载在 其他  
字(17.1k)|赞(0)|评价(0)|浏览(248)

前言

反射是 Go 语言比较重要的特性。虽然在大多数的应用和服务中并不常见,但是很多框架都依赖 Go 语言的反射机制实现简化代码的逻辑。因为 Go 语言的语法元素很少、设计简单,所以它没有特别强的表达能力,但是 Go 语言的 reflect 包能够弥补它在语法上的一些劣势。

reflect 实现了运行时的反射能力,能够让程序操作不同类型的对象。反射包中有两对非常重要的函数和类型,reflect.TypeOf 能获取类型信息,reflect.ValueOf 能获取数据的运行时表示,另外两个类型是 Type 和 Value,它们与函数是一一对应的关系:

类型 Type 是反射包定义的一个接口,我们可以使用 reflect.TypeOf 函数获取任意变量的的类型,Type 接口中定义了一些有趣的方法,MethodByName 可以获取当前类型对应方法的引用、Implements 可以判断当前类型是否实现了某个接口:

type Type interface {
        Align() int
        FieldAlign() int
        Method(int) Method
        MethodByName(string) (Method, bool)
        NumMethod() int
        ...
        Implements(u Type) bool
        ...
}

反射包中 Value 的类型与 Type 不同,它被声明成了结构体。这个结构体没有对外暴露的字段,但是提供了获取或者写入数据的方法:

type Value struct {
        // contains filtered or unexported fields
}
func (v Value) Addr() Value
func (v Value) Bool() bool
func (v Value) Bytes() []byte
...

反射包中的所有方法基本都是围绕着 Type 和 Value 这两个类型设计的。我们通过 reflect.TypeOf、reflect.ValueOf 可以将一个普通的变量转换成『反射』包中提供的 Type 和 Value,随后就可以使用反射包中的方法对它们进行复杂的操作。

三大法则

运行时反射是程序在运行期间检查其自身结构的一种方式。反射带来的灵活性是一把双刃剑,反射作为一种元编程方式可以减少重复代码,但是过量的使用反射会使我们的程序逻辑变得难以理解并且运行缓慢。我们在这一节中会介绍 Go 语言反射的三大法则,其中包括:

  • 从 interface{} 变量可以反射出反射对象;
  • 从反射对象可以获取 interface{} 变量;
  • 要修改反射对象,其值必须可设置;

第一法则

反射的第一法则是我们能将 Go 语言的 interface{} 变量转换成反射对象。很多读者可能会对这以法则产生困惑 ,为什么是从 interface{} 变量到反射对象?

当我们执行 reflect.ValueOf(1) 时,虽然看起来是获取了基本类型 int 对应的反射类型,但是由于 reflect.TypeOf、reflect.ValueOf 两个方法的入参都是 interface{} 类型,所以在方法执行的过程中发生了类型转换。

Go 语言的函数调用都是值传递的,变量会在函数调用时进行类型转换。基本类型 int 会转换成 interface{} 类型,这也就是为什么第一条法则是『从接口到反射对象』。

上面提到的 reflect.TypeOf 和 reflect.ValueOf 函数就能完成这里的转换,如果我们认为 Go 语言的类型和反射类型处于两个不同的『世界』,那么这两个函数就是连接这两个世界的桥梁。

我们通过以下例子简单介绍这两个函数的作用,reflect.TypeOf 获取了变量 author 的类型,reflect.ValueOf 获取了变量的值 draven。如果我们知道了一个变量的类型和值,那么就意味着知道了这个变量的全部信息。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    author := "draven"
    fmt.Println("TypeOf author:", reflect.TypeOf(author))
    fmt.Println("ValueOf author:", reflect.ValueOf(author))
}

$ go run main.go
TypeOf author: string
ValueOf author: draven

有了变量的类型之后,我们可以通过 Method 方法获得类型实现的方法,通过 Field 获取类型包含的全部字段。对于不同的类型,我们也可以调用不同的方法获取相关信息:

  • 结构体:获取字段的数量并通过下标和字段名获取字段 StructField;
  • 哈希表:获取哈希表的 Key 类型;
  • 函数或方法:获取入参和返回值的类型;

总而言之,使用 reflect.TypeOf 和 reflect.ValueOf 能够获取 Go 语言中的变量对应的反射对象。一旦获取了反射对象,我们就能得到跟当前类型相关数据和操作,并可以使用这些运行时获取的结构执行方法。

第二法则

反射的第二法则是我们可以从反射对象可以获取 interface{} 变量。既然能够将接口类型的变量转换成反射对象,那么一定需要其他方法将反射对象还原成接口类型的变量,reflect 中的 reflect.Value.Interface 方法就能完成这项工作:

不过调用 reflect.Value.Interface 方法只能获得 interface{} 类型的变量,如果想要将其还原成最原始的状态还需要经过如下所示的显式类型转换:

v := reflect.ValueOf(1)
v.Interface().(int)

从反射对象到接口值的过程就是从接口值到反射对象的镜面过程,两个过程都需要经历两次转换:

从接口值到反射对象:

  • 从基本类型到接口类型的类型转换;
  • 从接口类型到反射对象的转换;

从反射对象到接口值:

  • 反射对象转换成接口类型;
  • 通过显式类型转换变成原始类型;

当然不是所有的变量都需要类型转换这一过程。如果变量本身就是 interface{} 类型,那么它不需要类型转换,因为类型转换这一过程一般都是隐式的,所以我不太需要关心它,只有在我们需要将反射对象转换回基本类型时才需要显式的转换操作。

第三法则

Go 语言反射的最后一条法则是与值是否可以被更改有关,如果我们想要更新一个 reflect.Value,那么它持有的值一定是可以被更新的,假设我们有以下代码:

func main() {
    i := 1
    v := reflect.ValueOf(i)
    v.SetInt(10)
    fmt.Println(i)
}

$ go run reflect.go
panic: reflect: reflect.flag.mustBeAssignable using unaddressable value

goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x82, 0x1014c0)
    /usr/local/go/src/reflect/value.go:247 +0x180
reflect.flag.mustBeAssignable(...)
    /usr/local/go/src/reflect/value.go:234
reflect.Value.SetInt(0x100dc0, 0x414020, 0x82, 0x1840, 0xa, 0x0)
    /usr/local/go/src/reflect/value.go:1606 +0x40
main.main()
    /tmp/sandbox590309925/prog.go:11 +0xe0

运行上述代码会导致程序崩溃并报出 reflect: reflect.flag.mustBeAssignable using unaddressable value 错误,仔细思考一下就能够发现出错的原因,Go 语言的函数调用都是传值的,所以我们得到的反射对象跟最开始的变量没有任何关系,所以直接对它修改会导致崩溃。

想要修改原有的变量只能通过如下的方法:

func main() {
    i := 1
    v := reflect.ValueOf(&i)
    v.Elem().SetInt(10)
    fmt.Println(i)
}
$ go run reflect.go
10
  • 调用 reflect.ValueOf 函数获取变量指针;
  • 调用 reflect.Value.Elem 方法获取指针指向的变量;
  • 调用 reflect.Value.SetInt 方法更新变量的值:由于 Go 语言的函数调用都是值传递的,所以我们只能先获取指针对应的 reflect.Value,再通过 reflect.Value.Elem 方法迂回的方式得到可以被设置的变量,我们通过如下所示的代码理解这个过程:
func main() {
    i := 1
    v := &i
    *v = 10
}

如果不能直接操作 i 变量修改其持有的值,我们就只能获取 i 变量所在地址并使用 *v 修改所在地址中存储的整数。

API总结

反射类型

//reflect/type.go
type Type interface {
  // 该类型内存分配大小(内存对齐单位子节)
	Align() int

	// 该类型作为结构体字段时内存分配大小(内存对齐单位子节)
	FieldAlign() int

  // 根据index in [0, NumMethod())获取方法   按lexicographic排序
	Method(int) Method

 //  根据方法名获取方法
	MethodByName(string) (Method, bool)

	// 获取所有可用方法数量
	// 接口类型 获取包含未导出方法
	NumMethod() int

	// 返回类型名,未定义则为空
	Name() string

  // 返回类型所在包名,未定义则为空
	PkgPath() string

	// 错误类型所需子节数 unsafe.Sizeof.
	Size() uintptr

	// 返回类型名称字符串
	String() string

	// 返回此类型的kind类型
	Kind() Kind

	// 返回是否实现了u接口
	Implements(u Type) bool

	// 返回类型的值是否可分配给类型u
	AssignableTo(u Type) bool

	// 返回类型的值是否可以转换为u类型
	ConvertibleTo(u Type) bool

	// 返回类型的值是否可对比
	Comparable() bool

	// Methods applicable only to some types, depending on Kind.
	// The methods allowed for each kind are:
	//
	//	Int*, Uint*, Float*, Complex*: Bits
	//	Array: Elem, Len
	//	Chan: ChanDir, Elem
	//	Func: In, NumIn, Out, NumOut, IsVariadic.
	//	Map: Key, Elem
	//	Ptr: Elem
	//	Slice: Elem
	//	Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField

	// Bits returns the size of the type in bits.
	// It panics if the type's Kind is not one of the
	// sized or unsized Int, Uint, Float, or Complex kinds.
	Bits() int

    
	// Elem返回类型的元素类型
	// 不是 Array, Chan, Map, Ptr, or Slice. panic
	Elem() Type

  // ---------------   struct  ------------------------
	// 字段返回结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	Field(i int) StructField

	// 字段返回嵌套结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	FieldByIndex(index []int) StructField

	// 按名称返回字段
	FieldByName(name string) (StructField, bool)

 // 按过滤方法返回匹配字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)
  
	// 返回结构体字段总数
	// 不是 Struct panic.
	NumField() int
  // ---------------   struct  ------------------------

  // ---------------   func  --------------------
 //返回函数类型第i的输入参数
  // 不是Func painc
	In(i int) Type
  
  	// 返回方法输入参数总数
	// 不是Func painc
	NumIn() int

	// 返回函数输出参数总数
	// 不是Func painc
	NumOut() int

  // 返回函数类型第i的输出参数
  // 不是Func painc
	Out(i int) Type
  
  // 返回函数是否包含可变参数
  // 不是Func painc
	IsVariadic() bool
  // ---------------   func  --------------------
  
  
  // ---------------   Map  --------------------
	// 返回map的key类型
	// 不是Map panic
	Key() Type
  // ---------------   Map  --------------------
  
  
  // ---------------   Array  --------------------
	// 返回数组类型的长度 
	// 不是Array panic
	Len() int
  // ---------------   Array  --------------------

  // ---------------   chan  --------------------
	// 返回chan类型的方向,不是chan会panic
	ChanDir() ChanDir
  
  // ---------------   chan  --------------------

	common() *rtype
	uncommon() *uncommonType
}

反射对象

//reflect/value.go
type Value struct {
	// 值的类型
	typ *rtype
	// 数据指针
  // flagIndir设置时
	ptr unsafe.Pointer
  // flag保存有关该值的元数据
	flag
}


// 返回value是否可以被寻址
func (v Value) CanAddr() bool
// 类似& value寻址返回包装value
func (v Value) Addr() Value
// 返回value是否可以被修改 需要CanAddr
func (v Value) CanSet() bool
// 类似* value解引用后返回包装value 
// 需要prt interface
func (v Value) Elem() Value

// 方法调用
func (v Value) Call(in []Value) []Value
// 方法调用 可变参数使用数组传入
func (v Value) CallSlice(in []Value) []Value

func (v Value) TryRecv() (x Value, ok bool)
func (v Value) Recv() (x Value, ok bool)
func (v Value) TrySend(x Value) bool 
func (v Value) Send(x Value) 
func (v Value) Close()

func (v Value) Field(i int) Value
func (v Value) FieldByIndex(index []int) Value
func (v Value) FieldByName(name string) Value
func (v Value) FieldByNameFunc(match func(string) bool) Value
func (v Value) Method(i int) Value
func (v Value) NumMethod() int
func (v Value) NumField() int
func (v Value) MethodByName(name string) Value 

func (v Value) Index(i int) Value
func (v Value) Type() Type
func (v Value) Kind() Kind
func (v Value) Convert(t Type) Value
func (v Value) UnsafeAddr() uintptr


// 返回子节数组
func (v Value) Bytes() []byte
func (v Value) SetBytes(x []byte)
// 返回string类型
func (v Value) String() string
func (v Value) SetString(x string) 
// 返回interface类型
func (v Value) CanInterface() bool
func (v Value) Interface() (i interface{})
func (v Value) InterfaceData() [2]uintptr
func (v Value) Set(x Value)
// 返回float64
func (v Value) Float() float64
func (v Value) SetFloat(x float64)
func (v Value) OverflowFloat(x float64) bool
// 返回int64
func (v Value) Int() int64
func (v Value) SetInt(x int64)
func (v Value) OverflowInt(x int64) bool
// 返回uint64
func (v Value) Uint() uint64
func (v Value) SetUint(x uint64)
func (v Value) OverflowUint(x uint64) bool
// 返回bool
func (v Value) Bool() bool
func (v Value) SetBool(x bool) 
// 返回complex128
func (v Value) Complex() complex128
func (v Value) SetComplex(x complex128)
func (v Value) OverflowComplex(x complex128) bool

// map操作
func (v Value) SetMapIndex(key, elem Value)
func (v Value) MapIndex(key Value) Value
func (v Value) MapKeys() []Value
func (v Value) MapRange() *MapIter


// chan func interface map ptr slice 返回值是否位nil
func (v Value) IsNil() bool
// 返回value本身是否有效 
func (v Value) IsValid() bool 
// 返回value是否位该对应类型的零值
func (v Value) IsZero() bool


func (v Value) Len() int
func (v Value) SetLen(n int)
func (v Value) Cap() int
func (v Value) SetCap(n int) 

func (v Value) Pointer() uintptr
func (v Value) SetPointer(x unsafe.Pointer)

func (v Value) Slice(i, j int) Value
func (v Value) Slice3(i, j, k int) Value


type flag uintptr

const (
	// tflagUncommon means that there is a pointer, *uncommonType,
	// just beyond the outer type structure.
	//
	// For example, if t.Kind() == Struct and t.tflag&tflagUncommon != 0,
	// then t has uncommonType data and it can be accessed as:
	//
	//	type tUncommon struct {
	//		structType
	//		u uncommonType
	//	}
	//	u := &(*tUncommon)(unsafe.Pointer(t)).u
	tflagUncommon tflag = 1 << 0

	// tflagExtraStar means the name in the str field has an
	// extraneous '*' prefix. This is because for most types T in
	// a program, the type *T also exists and reusing the str data
	// saves binary size.
	tflagExtraStar tflag = 1 << 1

	// tflagNamed means the type has a name.
	tflagNamed tflag = 1 << 2

	// tflagRegularMemory means that equal and hash functions can treat
	// this type as a single region of t.size bytes.
	tflagRegularMemory tflag = 1 << 3
)

// 实现Type接口
type rtype struct {
	size       uintptr
	ptrdata    uintptr // number of bytes in the type that can contain pointers
	hash       uint32  // hash of type; avoids computation in hash tables
	tflag      tflag   // extra type information flags
	align      uint8   // alignment of variable with this type
	fieldAlign uint8   // alignment of struct field with this type
	kind       uint8   // enumeration for C
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal     func(unsafe.Pointer, unsafe.Pointer) bool
	gcdata    *byte   // garbage collection data
	str       nameOff // string form
	ptrToThis typeOff // type for pointer to this type, may be zero
}

// 获取当前类型名称
func (t *rtype) String() string {
	s := t.nameOff(t.str).name()
	if t.tflag&tflagExtraStar != 0 {
		return s[1:]
	}
	return s
}

// reflect/value.go
type emptyInterface struct {
	typ  *rtype       
	word unsafe.Pointer
}

反射种类

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.TypeOf 能获取类型信息
func TypeOf(i interface{}) Type {
	eface := *(*emptyInterface)(unsafe.Pointer(&i))
	return toType(eface.typ)
}

func toType(t *rtype) Type {
	if t == nil {
		return nil
	}
	return t
}
  • reflect.ValueOf 能获取数据的运行时表示
func ValueOf(i interface{}) Value {
	if i == nil {
		return Value{}
	}

  // 当前值逃逸堆上
	escapes(i)

	return unpackEface(i)
}

func unpackEface(i interface{}) Value {
	e := (*emptyInterface)(unsafe.Pointer(&i))
	t := e.typ
	if t == nil {
		return Value{}
	}
	f := flag(t.Kind())
	if ifaceIndir(t) {
		f |= flagIndir
	}
	return Value{t, e.word, f}
}
// 获取反射对象
hello := "hello"
fmt.Println( reflect.TypeOf(hello),reflect.ValueOf(hello))
fmt.Printf("%T %v",hello,hello)
//string hello
//string hello

//反射对象获取值
// reflect.Value -> interface{}(reflect.Inerface) -> type
// type -> interface{} -> reflect.Value(reflect.ValueOf)
v := reflect.ValueOf(1)
i := v.Interface().(int)
fmt.Printf("%T %v \n",v,v)
fmt.Printf("%T %v \n",i,i)
//reflect.Value 1 
//int 1 

//修改反射对象
a := 1
v1 := reflect.ValueOf(&a)
v1.Elem().SetInt(10)
fmt.Printf("%T %v \n",a,a)
// 等价  v1:=&a *v1=10
//int 10

接口

获取一个接口的type reflect.TypeOf((*<interface>)(nil)).Elem()

type Type struct{
  // 根据index in [0, NumMethod())获取方法   按lexicographic排序
	Method(int) Method

 //  根据方法名获取方法
	MethodByName(string) (Method, bool)

	// 获取所有可用方法数量
	// 接口类型 获取包含未导出方法
	NumMethod() int
  
}

例子

package main

import (
	"reflect"
)

type i interface {
	F1() string
	F3() string
}
type s struct {
	h    string
	Name string
	Type int `json:"type" id:"100"`
}

func (h s) F1() string {
	return h.Name
}

func (h s) f2() string {
	return h.Name
}

func (h *s) F3() string {
	return h.Name
}

func main() {

	t1 := reflect.TypeOf((*i)(nil)).Elem()
	s1 := reflect.TypeOf(&s{})
	s2 := reflect.TypeOf(s{})

	println(s1.Implements(t1))
	println(s2.Implements(t1))

	println(t1.Align())
	println(t1.FieldAlign())
	println(t1.NumMethod())

	for i := 0; i < t1.NumMethod(); i++ {
		f := t1.Method(i)
		println("---------")
		println(f.Name)
		println(f.Type.String())
		println(f.Index)
		println(f.Func.String())
		println("---------")
	}

	m1, b := t1.MethodByName("F1")
	println(b, m1.Name)

}
// true
// false
// 8
// 8
// 2
// ---------
// F1
// func() string
// 0
// <invalid Value>
// ---------
// ---------
// F3
// func() string
// 1
// <invalid Value>
// ---------
// true F1

结构体

// reflect/type.go
// StructField
type StructField struct {
    Name string          // 字段名
    PkgPath string       // 字段路径
    Type      Type       // 字段反射类型对象
    Tag       StructTag  // 字段的结构体标签
    Offset    uintptr    // 字段在结构体中的相对偏移
    Index     []int      // Type.FieldByIndex中的返回的索引值
    Anonymous bool       // 是否为匿名字段
}

// StructTag 
type StructTag string

//根据 Tag 中的键获取对应的值
func (tag StructTag) Get(key string) string {}

//根据 Tag 中的键,查询值是否存在
func (tag StructTag) Lookup(key string) (value string, ok bool) {}

// 获取StructField
type Type interface {
  
  // 该类型作为结构体字段时内存分配大小(内存对齐单位子节)
	FieldAlign() int

  // 根据index in [0, NumMethod())获取方法   按lexicographic排序
	Method(int) Method

 //  根据方法名湖区方法
	MethodByName(string) (Method, bool)

	// 获取所有可用方法数量
	// 接口类型 获取包含未导出方法
	NumMethod() int
  
  	// 返回是否实现了u接口
	Implements(u Type) bool

	// 返回类型的值是否可分配给类型u
	AssignableTo(u Type) bool

	// 返回类型的值是否可以转换为u类型
	ConvertibleTo(u Type) bool
  
  // ---------------   struct  ------------------------
	// 字段返回结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	Field(i int) StructField

	// 字段返回嵌套结构类型的第i个字段。
	// 不是struct 或者下表越界 painc
	FieldByIndex(index []int) StructField

	// 按名称返回字段
	FieldByName(name string) (StructField, bool)

 // 按过滤方法返回匹配字段
	FieldByNameFunc(match func(string) bool) (StructField, bool)
  
	// 返回结构体字段总数
	// 不是 Struct panic.
	NumField() int
  // ---------------   struct  ------------------------
}

例子

package main

import (
	"reflect"
	"strings"
)

type i interface{
    F1() string
}
type s struct {
	h    string
	Name string
	Type int `json:"type" id:"100"`
}

func (h s)F1() string {
	return h.Name
}

func (h s)f2() string {
	return h.Name
}

func (h *s)F3() string {
	return h.Name
}

func main() {
	h := reflect.TypeOf(s{})
  hptr:=reflect.TypeOf(&s{})
	i1:= reflect.TypeOf((*i)(nil)).Elem()

	println(h.Align())
	println(h.FieldAlign())
	println(h.NumField())
	println(h.NumMethod())
	println(h.AssignableTo(i1))
	println(h.Implements(i1))
	println(h.ConvertibleTo(i1))
  
  println(hptr.AssignableTo(i1))
	println(hptr.Implements(i1))
	println(hptr.ConvertibleTo(i1))


	for i:=0;i<h.NumField();i++{
		f:=h.Field(i)
		println("---------")
		println(f.Name)
		println(f.Type.FieldAlign())
		println(f.Type.String())
		println(f.Index)
		println(f.Anonymous)
		println(f.Offset)
		println(f.Tag)
		println("---------")
	}

	for i:=0;i<h.NumMethod();i++{
		f:=h.Method(i)
		println("---------")
		println(f.Name)
		println(f.Type.String())
		println(f.Index)
		println(f.Func.String())
		println("---------")
	}

	m1, b := h.MethodByName("f2")
	println(b,m1.Name)

	s1 ,b := h.FieldByName("h")
	println(b,s1.Name)

	s2,b:=h.FieldByNameFunc(func(s2 string) bool {
		// 必须唯一匹配
		return strings.Contains(s2,"e")
	})
	println(b,s2.Name)


}

// 8
// 8
// 3
// 1
// true
// true
// true
// ---------
// h
// 8
// string
// [1/1]0xc00001c0c0
// false
// 0

// ---------
// ---------
// Name
// 8
// string
// [1/1]0xc00001c0c8
// false
// 16

// ---------
// ---------
// Type
// 8
// int
// [1/1]0xc00001c0d0
// false
// 32
// json:"type" id:"100"
// ---------
// ---------
// F1
// func(main.s) string
// 0
// <func(main.s) string Value>
// ---------
// false 
// true h
// false

指针

  • 通过elem()获得指针指向的类型
// 获取StructField
type Type interface {
  // Elem返回类型的元素类型
  // 不是 Array, Chan, Map, Ptr, or Slice. panic
  Elem() Type
}

例子

package main
import (
    "fmt"
    "reflect"
)
func main() {
  s:=&h{Name:"hello"}
	v2 := reflect.TypeOf(s)
	fmt.Printf("%s %s \n",v2.Name(),v2.Kind())
	fmt.Printf("%s %s \n",v2.Elem().Name(),v2.Elem().Kind())
}
// ptr
// h struct

函数

func Type interface {
// ---------------   func  --------------------
 //返回函数类型第i的输入参数
  // 不是Func painc
	In(i int) Type
  
  	// 返回方法输入参数总数
	// 不是Func painc
	NumIn() int

	// 返回函数输出参数总数
	// 不是Func painc
	NumOut() int

  // 返回函数类型第i的输出参数
  // 不是Func painc
	Out(i int) Type
  
  // 返回函数是否包含可变参数
  // 不是Func painc
	IsVariadic() bool
  // ---------------   func  --------------------
}

func Value struct{}

// 方法调用
func (v Value) Call(in []Value) []Value
// 方法调用 可变参数使用数组传入
func (v Value) CallSlice(in []Value) []Value

例子

package main

import (
	"reflect"
)

func main() {
	h := func(p1 int, p2 ...string) string { return fmt.Sprint(p1, p2) }
	t := reflect.TypeOf(h)
	v := reflect.ValueOf(h)
	println(t.IsVariadic())     // 是否包含可变参数
	println(t.Kind().String())
	println(t.NumIn())          // 输入参数
	for i := 0; i < t.NumIn(); i++ {
		p := t.In(i)           // 第i个输入参数
		println(p.Kind().String())
	}
	println(t.NumOut())         // 输出参数
	for i := 0; i < t.NumOut(); i++ {
		o := t.Out(i)           // 第i个输出参数
		println(o.Kind().String())
	}

	// 参数调用
	call := v.Call([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf("1"), reflect.ValueOf("1")})
	for _, value := range call {
		println(value.String())
	}

	// 可变参数调用
	calls := v.CallSlice([]reflect.Value{reflect.ValueOf(1), reflect.ValueOf([]string{"1", ""})})
	for _, value := range calls {
		println(value.String())
	}
}
//true
//func
//2
//int
//slice
//1
// string
// 1 [1 1]
// 1 [1]

函数调用原理

1.通过 reflect.funcLayout 计算当前函数需要的参数和返回值的栈布局,也就是每一 	个参数和返回值所占的空间大小;
2.如果当前函数有返回值,需要为当前函数的参数和返回值分配一片内存空间 args;
3.如果当前函数是方法,需要向将方法的接收接收者者拷贝到 args 内存中;
4.将所有函数的参数按照顺序依次拷贝到对应 args 内存中
	a.使用 reflect.funcLayout 返回的参数计算参数在内存中的位置;
	b.将参数拷贝到内存空间中;
5.call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
	a.frametype 栈类型
  b.fn 函数指针
  c.args 参数内存空间
  d.uint32(frametype.size) 栈大小
  e.uint32(retOffset)  返回值偏移量

函数返回原理

1.如果函数没有任何返回值,会直接清空 args 中的全部内容来释放内存空间;
2.如果当前函数有返回值;
	a.将 args 中与输入参数有关的内存空间清空;
	b.创建一个 nout 长度的切片用于保存由反射对象构成的返回值数组;
	c.从函数对象中获取返回值的类型和内存大小,将 args 内存中的数据转换成 reflect.Value 类型并存储到切片中;

var ret []Value
	if nout == 0 {
		typedmemclr(frametype, args)
		framePool.Put(args)
	} else {
		typedmemclrpartial(frametype, args, 0, retOffset)
		ret = make([]Value, nout)
		off = retOffset
		for i := 0; i < nout; i++ {
			tv := t.Out(i)
			a := uintptr(tv.Align())
			off = (off + a - 1) &^ (a - 1)
			if tv.Size() != 0 {
				fl := flagIndir | flag(tv.Kind())
				ret[i] = Value{tv.common(), add(args, off, "tv.Size() != 0"), fl}
			} else {
				ret[i] = Zero(tv)
			}
			off += tv.Size()
		}
	}

	return ret
}

相关文章