GoLang反射

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GoLang反射相关的知识,希望对你有一定的参考价值。

GoLang反射


前言

反射是 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
	

  // 当

以上是关于GoLang反射的主要内容,如果未能解决你的问题,请参考以下文章

golang reflect反射(一):interface接口的入门(大白话)

Golang 反射

golang的reflect

理解golang反射(reflection in Go)

(四十五)golang--反射

GoLang反射