Jquery中文网 www.jquerycn.cn
Jquery中文网 >  后端编程  >  Go语言  >  正文 golang 结构体断言_Golang中的reflect原理

golang 结构体断言_Golang中的reflect原理

发布时间:2021-04-25   编辑:www.jquerycn.cn
jquery中文网为您提供golang 结构体断言,Golang中的reflect原理等资源,欢迎您收藏本站,我们将为您提供最新的golang 结构体断言,Golang中的reflect原理资源

反射(reflect)是在计算机程序运行时,访问,检查,修改它自身的一种能力,是元编程的一种形式。在Java等语言中都很好地支持了反射。Golang也实现了反射,主要核心位于reflect包,官方文档为:

https://golang.org/pkg/reflect/​golang.org

本文将主要介绍Golang中的反射原理和支持的反射操作。

1. reflect原理:结构体与关系

Golang是强类型语言,每一个对象都有具体的静态类型。为什么说是静态类型呢?举个例子,如下代码:

type MyInt int

var a int
var b MyInt

a和b在Go中会被认为是不同的类型,即不会被隐式转换。另外,在Golang中的对象其实是同时记录了两个信息:变量的真实值,与该变量的类型描述。具体地,interface {}在内部是通过emptyInterface结构体表示的,结构体的定义如下:

type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}

其中, typ为类型信息,word为指针。

另外interface{}是一个没有函数定义的接口定义,Golang中的继承实现是通过比较函数判断的。也就是说,所有的结构体都实现了默认接口interface{},这也是为什么所有值都能够隐式赋值给interface{}的原因。

以上的结构体便是Golang中反射的核心,也就是通过操作该结构来进行反射运算,包括获取对象的类型信息(rtype)和具体值(word指针),rtype的定义如下:

// rtype is the common implementation of most values.
// It is embedded in other struct types.
//
// rtype must be kept in sync with ../runtime/type.go:/^type._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
}

定义了类型需要的数据。

1.1 reflect中的结构体

在Golang的反射中,另外两个核心结构体是Type和Value。

Type是描述类型信息的接口,包括:结构体的对齐方式、方法、字段、包路径、与其他结构体的关系等,具体定义可以参考源码:

https://golang.org/src/reflect/type.go?s=1310:7552#L27​golang.org

Value是保存了对象的类型、指针和其他元数据,具体定义如下:

type Value struct {
	// typ holds the type of the value represented by a Value.
	typ *rtype

	// Pointer-valued data or, if flagIndir is set, pointer to data.
	// Valid when either flagIndir is set or typ.pointers() is true.
	ptr unsafe.Pointer

	// flag holds metadata about the value.
	// The lowest bits are flag bits:
	//	- flagStickyRO: obtained via unexported not embedded field, so read-only
	//	- flagEmbedRO: obtained via unexported embedded field, so read-only
	//	- flagIndir: val holds a pointer to the data
	//	- flagAddr: v.CanAddr is true (implies flagIndir)
	//	- flagMethod: v is a method value.
	// The next five bits give the Kind of the value.
	// This repeats typ.Kind() except for method values.
	// The remaining 23  bits give a method number for method values.
	// If flag.kind() != Func, code can assume that flagMethod is unset.
	// If ifaceIndir(typ), code can assume that flagIndir is set.
	flag

	// A method value represents a curried method invocation
	// like r.Read for some receiver r. The typ val flag bits describe
	// the receiver r, but the flag's Kind bits say Func (methods are
	// functions), and the top bits of the flag give the method number
	// in r's type's method table.
}

1.2 reflect对象的关系

reflect中的对象关系如下图所示,Type和Value称为反射对象,interface{}和Special Type是应用程序中的对象,其中,Special Type指应用程序中的具体类型。具体关系如下:

反射对象关系图

1) 从接口值到反射对象

interface{} -> Type: 通过reflect.TypeOf(interface{})获得interface的类型信息对象;

interface{} -> Value:通过reflect.ValueOf(interface{})获得interface的Value反射类型对象;

2) 从反射对象到接口值

Value->interface{}:通过Value.Interface()方法可以获得值对象Value对应的接口;注意,这里不能够直接获得具体类型,如果要获得具体类型,还需要显式地进行转换。例如,

var f float64 = 3.1415
v := reflect.ValueOf(f)   // f 隐式地被转成了interface{}
y := v.Interface().(float64) // y的类型是float64

其中1)和2)两条关系也是Golang反射中三条大规则中的前两条。另外第三条是:

3) 想要修改一个反射对象,那么该值必须是可以被设置的

这个可以一个例子进行说明。如下:

var f float64 = 3.1415
v := reflect.ValueOf(f)   // f 隐式地被转成了interface{}
v.SetFloat(2.873)      // Error: 发生Panic

以上在最后一行代码将会抛出异常:

panic: reflect.Value.SetFloat using unaddressable value

也就是说,Value v指向的不是一个可寻址的值,简单地说就是不是一个地址块。但是如果改成如下代码:

var f float64 = 3.1415
v := reflect.ValueOf(&f)   // 传了f的指针,&f 隐式地被转成了interface{}
v.SetFloat(2.873)      // 成功修改

综上,也就是说当Value中管理的值是一个可被寻址的值那么改置便是一个可被修改的Value。

或者换一个方式去理解,在Golang中方法调用是值传递,然后,假如我们想要该一个方法中修改某一个对象的值,那么我们应该将指向该值的指针传入,而不是直接将值传入。

4) Type/Value转换

Value->Type:可以通过Value.Type()方法获得;而Type->Value是指创建一个Type的实例对象,则可以通过reflect.New(typ)等方法创建。

2. reflect中的结构体与方法

这里分五个维度进行介绍reflect中的结构和方法,便于理解反射的使用方法。这些操作最终都会落到前面定义的结构体emptyInterface,除在外层封装中变能够确定的方法外。

2.1 结构体

reflect中的结构体主要包括:Type,Value,ChanDir,Kind,MapIter,Method,SelectCase,SelectDir,SliceHeader,StringHeader,StructField,StructTag,ValueError等。其中,Type和Value之前已经介绍过了。

  • ChanDir:管道的方向,有三个值:RecvDir/SendDir/BothDir,分别为接受,发送,双向;
  • Kind:Type中的类型信息,包括:Invalid, Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Float32, Float64, Complex64, Complex128, Array, Chan, Func, Interface, Map, Ptr, Slice, String, Struct, UnsafePointer,
  • MapIter:Map的迭代器,包括三个方法:Key、Value、Next
  • Method:描述方法的信息,包括:方法名,包路径,类型,函数,所处的下表;
  • SelectCase:描述select 操作的信息,case的方向SelectDir,使用的Channel,发送的值Send;
  • SelectDir:描述SelectCase中的方向,有三个值:SelectSend/SelectRecv/SelectDefault
  • SliceHeader:描述切片Slice的信息,包括指针,长度,容量;
  • StringHeader:描述字符串string的信息,包括指针,长度;
  • StructField:描述结构体中的域field中的信息,包括:域名,包路径,类型,标签Tag,在结构体中的偏移量offset,Type.FieldByIndex中的下标index,是否是匿名;
  • StructTag:描述标签信息,有两个方法:Get、Lookup
  • ValueError:在调用一个Value不支持的方法时会报错,并记录到ValueError中。

2.2 reflect静态方法

reflect的静态方法主要用于反射对象的的操作,包括如下:

  • reflect.Copy(dst, src Value) int:将src对象(Slice或Array)复制给dst对象,返回复制的个数;
  • func DeepEqual(x, y interface{}) bool:比较两个对象是否是“深度相等”。具体如何比较可以参考:https://golang.org/pkg/reflect/#DeepEqual
  • func Swapper(slice interface{}) func(i, j int):生成一个Swapper交换方法,必须为slice;

2.3 域Type相关的方法

该类方法主要定义在https://golang.org/src/reflect/type.go?s=78900:78939#L2811中,包括,返回值都是Type:

  • reflect.ArrayOf:创建一个指定Type和个数的数组;
  • reflect.ChanOf:创建一个类型和方向的管道类型;
  • reflect.FuncOf:创建一个指定输入/输出/是否可变(variadic)的函数定义;
  • reflect.MapOf:创建一个指定key/value类型的Map类型;
  • reflect.PtrTo:创建一个类型的指针;
  • reflect.SliceOf:创建一个类型的Slice类型;
  • reflect.StructOf:创建一个指定StructField 列表的结构体定义类型;
  • reflect.TypeOf:获得interface的类型;

2.4 值Value相关静态方法

该类方法主要定义在https://golang.org/src/reflect/value.go?s=60334:60372#L2014中,包括:

  • reflect.Append:将值append到一个Slice中,并且返回结果;
  • reflect.AppendSlice:将一个slice append到slice中;
  • reflect.Indirect:返回该Value的指向对象,如果是nil,返回零值,如果非指针,返回该值;
  • reflect.MakeChan:创建一个执行类型和大小的channel;
  • reflect.MakeFunc:在指定类型上创建一个指定定义的函数;
  • reflect.MakeMap:创建一个指定类型的map;
  • reflect.MakeMapWithSize:同上,指定大小;
  • reflect.MakeSlice:创建Slice;
  • reflect.New:创建一个指定类型的实例对象;
  • reflect.NewAt:指定了指针类型?
  • reflect.Select:创建一个select操作,需指定SelectCase
  • reflect.ValueOf:获得接口interface{}的Value;
  • reflect.Zero:创建一个指定类型的零值;

2.5 Value的实例方法

这里将不一一介绍Value的实例方法,大致可以分成三类:

  • 判断性方法:判断是否具有某些特性/能力;
  • 访问性方法:访问值的一些属性;
  • 修改性方法:修改Value中特性/值的方法;

这里介绍一个函数Elem(),该函数返回的是interface{}中包含的值或指针指向的值,如果value的类型不是reflect.Ptr,那么将返回零值。

具体如下:

func (v Value) Addr() Value
func (v Value) Bool() bool
func (v Value) Bytes() []byte
func (v Value) Call(in []Value) []Value// 最后会进入汇编代码进行方法调用
func (v Value) CallSlice(in []Value) []Value
func (v Value) CanAddr() bool
func (v Value) CanInterface() bool
func (v Value) CanSet() bool
func (v Value) Cap() int
func (v Value) Close()
func (v Value) Complex() complex128
func (v Value) Convert(t Type) Value
func (v Value) Elem() Value
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) Float() float64
func (v Value) Index(i int) Value
func (v Value) Int() int64
func (v Value) Interface() (i interface{})
func (v Value) InterfaceData() [2]uintptr
func (v Value) IsNil() bool
func (v Value) IsValid() bool
func (v Value) IsZero() bool
func (v Value) Kind() Kind
func (v Value) Len() int
func (v Value) MapIndex(key Value) Value
func (v Value) MapKeys() []Value
func (v Value) MapRange() *MapIter
func (v Value) Method(i int) Value
func (v Value) MethodByName(name string) Value
func (v Value) NumField() int
func (v Value) NumMethod() int
func (v Value) OverflowComplex(x complex128) bool
func (v Value) OverflowFloat(x float64) bool
func (v Value) OverflowInt(x int64) bool
func (v Value) OverflowUint(x uint64) bool
func (v Value) Pointer() uintptr
func (v Value) Recv() (x Value, ok bool)
func (v Value) Send(x Value)
func (v Value) Set(x Value)
func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetCap(n int)
func (v Value) SetComplex(x complex128)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
func (v Value) SetLen(n int)
func (v Value) SetMapIndex(key, elem Value)
func (v Value) SetPointer(x unsafe.Pointer)
func (v Value) SetString(x string)
func (v Value) SetUint(x uint64)
func (v Value) Slice(i, j int) Value
func (v Value) Slice3(i, j, k int) Value
func (v Value) String() string
func (v Value) TryRecv() (x Value, ok bool)
func (v Value) TrySend(x Value) bool
func (v Value) Type() Type
func (v Value) Uint() uint64
func (v Value) UnsafeAddr() uintptr

3. 总结

最后简单总结一下,本文首先介绍了Golang中反射的原理,包括其中的核心结构体和关系;然后介绍了Golang中reflect包下包含的结构体,静态函数,Type静态函数,Value静态函数和Value的实例函数。

参考

https://golang.org/pkg/reflect/https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/

Wenguang Liu:Golang中Routine闭包中的一个坑​zhuanlan.zhihu.com
到此这篇关于“golang 结构体断言_Golang中的reflect原理”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 结构体断言_Golang中的reflect原理
golang标准库reflect包实现操作任意类型对象
golang的反射机制与实践(上)
Golang 结构体判空
golang 初始化并赋值_Golang | 既是接口又是类型,interface是什么神仙用法?
golang map 锁_Golang线程安全的map
golang key map 所有_Golang基础教程——map篇
golang key map 所有_golang推断map中指定key是不是存在_后端开发
图解 Go 反射实现原理
golang key map 所有_Golang面试知识点总结

[关闭]