golang 结构体断言_Golang中的reflect原理
反射(reflect)是在計(jì)算機(jī)程序運(yùn)行時(shí),訪問(wèn),檢查,修改它自身的一種能力,是元編程的一種形式。在Java等語(yǔ)言中都很好地支持了反射。Golang也實(shí)現(xiàn)了反射,主要核心位于reflect包,官方文檔為:
https://golang.org/pkg/reflect/?golang.org本文將主要介紹Golang中的反射原理和支持的反射操作。
1. reflect原理:結(jié)構(gòu)體與關(guān)系
Golang是強(qiáng)類型語(yǔ)言,每一個(gè)對(duì)象都有具體的靜態(tài)類型。為什么說(shuō)是靜態(tài)類型呢?舉個(gè)例子,如下代碼:
type MyInt intvar a int var b MyInta和b在Go中會(huì)被認(rèn)為是不同的類型,即不會(huì)被隱式轉(zhuǎn)換。另外,在Golang中的對(duì)象其實(shí)是同時(shí)記錄了兩個(gè)信息:變量的真實(shí)值,與該變量的類型描述。具體地,interface {}在內(nèi)部是通過(guò)emptyInterface結(jié)構(gòu)體表示的,結(jié)構(gòu)體的定義如下:
type emptyInterface struct {typ *rtypeword unsafe.Pointer }其中, typ為類型信息,word為指針。
另外interface{}是一個(gè)沒有函數(shù)定義的接口定義,Golang中的繼承實(shí)現(xiàn)是通過(guò)比較函數(shù)判斷的。也就是說(shuō),所有的結(jié)構(gòu)體都實(shí)現(xiàn)了默認(rèn)接口interface{},這也是為什么所有值都能夠隱式賦值給interface{}的原因。
以上的結(jié)構(gòu)體便是Golang中反射的核心,也就是通過(guò)操作該結(jié)構(gòu)來(lái)進(jìn)行反射運(yùn)算,包括獲取對(duì)象的類型信息(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 uintptrptrdata uintptr // number of bytes in the type that can contain pointershash uint32 // hash of type; avoids computation in hash tablestflag tflag // extra type information flagsalign uint8 // alignment of variable with this typefieldAlign uint8 // alignment of struct field with this typekind 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) boolgcdata *byte // garbage collection datastr nameOff // string formptrToThis typeOff // type for pointer to this type, may be zero }定義了類型需要的數(shù)據(jù)。
1.1 reflect中的結(jié)構(gòu)體
在Golang的反射中,另外兩個(gè)核心結(jié)構(gòu)體是Type和Value。
Type是描述類型信息的接口,包括:結(jié)構(gòu)體的對(duì)齊方式、方法、字段、包路徑、與其他結(jié)構(gòu)體的關(guān)系等,具體定義可以參考源碼:
https://golang.org/src/reflect/type.go?s=1310:7552#L27?golang.orgValue是保存了對(duì)象的類型、指針和其他元數(shù)據(jù),具體定義如下:
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對(duì)象的關(guān)系
reflect中的對(duì)象關(guān)系如下圖所示,Type和Value稱為反射對(duì)象,interface{}和Special Type是應(yīng)用程序中的對(duì)象,其中,Special Type指應(yīng)用程序中的具體類型。具體關(guān)系如下:
反射對(duì)象關(guān)系圖1) 從接口值到反射對(duì)象
interface{} -> Type: 通過(guò)reflect.TypeOf(interface{})獲得interface的類型信息對(duì)象;
interface{} -> Value:通過(guò)reflect.ValueOf(interface{})獲得interface的Value反射類型對(duì)象;
2) 從反射對(duì)象到接口值
Value->interface{}:通過(guò)Value.Interface()方法可以獲得值對(duì)象Value對(duì)應(yīng)的接口;注意,這里不能夠直接獲得具體類型,如果要獲得具體類型,還需要顯式地進(jìn)行轉(zhuǎn)換。例如,
var f float64 = 3.1415 v := reflect.ValueOf(f) // f 隱式地被轉(zhuǎn)成了interface{} y := v.Interface().(float64) // y的類型是float64其中1)和2)兩條關(guān)系也是Golang反射中三條大規(guī)則中的前兩條。另外第三條是:
3) 想要修改一個(gè)反射對(duì)象,那么該值必須是可以被設(shè)置的
這個(gè)可以一個(gè)例子進(jìn)行說(shuō)明。如下:
var f float64 = 3.1415 v := reflect.ValueOf(f) // f 隱式地被轉(zhuǎn)成了interface{} v.SetFloat(2.873) // Error: 發(fā)生Panic以上在最后一行代碼將會(huì)拋出異常:
panic: reflect.Value.SetFloat using unaddressable value也就是說(shuō),Value v指向的不是一個(gè)可尋址的值,簡(jiǎn)單地說(shuō)就是不是一個(gè)地址塊。但是如果改成如下代碼:
var f float64 = 3.1415 v := reflect.ValueOf(&f) // 傳了f的指針,&f 隱式地被轉(zhuǎn)成了interface{} v.SetFloat(2.873) // 成功修改綜上,也就是說(shuō)當(dāng)Value中管理的值是一個(gè)可被尋址的值那么改置便是一個(gè)可被修改的Value。
或者換一個(gè)方式去理解,在Golang中方法調(diào)用是值傳遞,然后,假如我們想要該一個(gè)方法中修改某一個(gè)對(duì)象的值,那么我們應(yīng)該將指向該值的指針傳入,而不是直接將值傳入。
4) Type/Value轉(zhuǎn)換
Value->Type:可以通過(guò)Value.Type()方法獲得;而Type->Value是指創(chuàng)建一個(gè)Type的實(shí)例對(duì)象,則可以通過(guò)reflect.New(typ)等方法創(chuàng)建。
2. reflect中的結(jié)構(gòu)體與方法
這里分五個(gè)維度進(jìn)行介紹reflect中的結(jié)構(gòu)和方法,便于理解反射的使用方法。這些操作最終都會(huì)落到前面定義的結(jié)構(gòu)體emptyInterface,除在外層封裝中變能夠確定的方法外。
2.1 結(jié)構(gòu)體
reflect中的結(jié)構(gòu)體主要包括:Type,Value,ChanDir,Kind,MapIter,Method,SelectCase,SelectDir,SliceHeader,StringHeader,StructField,StructTag,ValueError等。其中,Type和Value之前已經(jīng)介紹過(guò)了。
- ChanDir:管道的方向,有三個(gè)值:RecvDir/SendDir/BothDir,分別為接受,發(fā)送,雙向;
- 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的迭代器,包括三個(gè)方法:Key、Value、Next
- Method:描述方法的信息,包括:方法名,包路徑,類型,函數(shù),所處的下表;
- SelectCase:描述select 操作的信息,case的方向SelectDir,使用的Channel,發(fā)送的值Send;
- SelectDir:描述SelectCase中的方向,有三個(gè)值:SelectSend/SelectRecv/SelectDefault
- SliceHeader:描述切片Slice的信息,包括指針,長(zhǎng)度,容量;
- StringHeader:描述字符串string的信息,包括指針,長(zhǎng)度;
- StructField:描述結(jié)構(gòu)體中的域field中的信息,包括:域名,包路徑,類型,標(biāo)簽Tag,在結(jié)構(gòu)體中的偏移量offset,Type.FieldByIndex中的下標(biāo)index,是否是匿名;
- StructTag:描述標(biāo)簽信息,有兩個(gè)方法:Get、Lookup
- ValueError:在調(diào)用一個(gè)Value不支持的方法時(shí)會(huì)報(bào)錯(cuò),并記錄到ValueError中。
2.2 reflect靜態(tài)方法
reflect的靜態(tài)方法主要用于反射對(duì)象的的操作,包括如下:
- reflect.Copy(dst, src Value) int:將src對(duì)象(Slice或Array)復(fù)制給dst對(duì)象,返回復(fù)制的個(gè)數(shù);
- func DeepEqual(x, y interface{}) bool:比較兩個(gè)對(duì)象是否是“深度相等”。具體如何比較可以參考:https://golang.org/pkg/reflect/#DeepEqual
- func Swapper(slice interface{}) func(i, j int):生成一個(gè)Swapper交換方法,必須為slice;
2.3 域Type相關(guān)的方法
該類方法主要定義在https://golang.org/src/reflect/type.go?s=78900:78939#L2811中,包括,返回值都是Type:
- reflect.ArrayOf:創(chuàng)建一個(gè)指定Type和個(gè)數(shù)的數(shù)組;
- reflect.ChanOf:創(chuàng)建一個(gè)類型和方向的管道類型;
- reflect.FuncOf:創(chuàng)建一個(gè)指定輸入/輸出/是否可變(variadic)的函數(shù)定義;
- reflect.MapOf:創(chuàng)建一個(gè)指定key/value類型的Map類型;
- reflect.PtrTo:創(chuàng)建一個(gè)類型的指針;
- reflect.SliceOf:創(chuàng)建一個(gè)類型的Slice類型;
- reflect.StructOf:創(chuàng)建一個(gè)指定StructField 列表的結(jié)構(gòu)體定義類型;
- reflect.TypeOf:獲得interface的類型;
2.4 值Value相關(guān)靜態(tài)方法
該類方法主要定義在https://golang.org/src/reflect/value.go?s=60334:60372#L2014中,包括:
- reflect.Append:將值append到一個(gè)Slice中,并且返回結(jié)果;
- reflect.AppendSlice:將一個(gè)slice append到slice中;
- reflect.Indirect:返回該Value的指向?qū)ο?#xff0c;如果是nil,返回零值,如果非指針,返回該值;
- reflect.MakeChan:創(chuàng)建一個(gè)執(zhí)行類型和大小的channel;
- reflect.MakeFunc:在指定類型上創(chuàng)建一個(gè)指定定義的函數(shù);
- reflect.MakeMap:創(chuàng)建一個(gè)指定類型的map;
- reflect.MakeMapWithSize:同上,指定大小;
- reflect.MakeSlice:創(chuàng)建Slice;
- reflect.New:創(chuàng)建一個(gè)指定類型的實(shí)例對(duì)象;
- reflect.NewAt:指定了指針類型?
- reflect.Select:創(chuàng)建一個(gè)select操作,需指定SelectCase;
- reflect.ValueOf:獲得接口interface{}的Value;
- reflect.Zero:創(chuàng)建一個(gè)指定類型的零值;
2.5 Value的實(shí)例方法
這里將不一一介紹Value的實(shí)例方法,大致可以分成三類:
- 判斷性方法:判斷是否具有某些特性/能力;
- 訪問(wèn)性方法:訪問(wèn)值的一些屬性;
- 修改性方法:修改Value中特性/值的方法;
這里介紹一個(gè)函數(shù)Elem(),該函數(shù)返回的是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// 最后會(huì)進(jìn)入?yún)R編代碼進(jìn)行方法調(diào)用 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() uintptr3. 總結(jié)
最后簡(jiǎn)單總結(jié)一下,本文首先介紹了Golang中反射的原理,包括其中的核心結(jié)構(gòu)體和關(guān)系;然后介紹了Golang中reflect包下包含的結(jié)構(gòu)體,靜態(tài)函數(shù),Type靜態(tài)函數(shù),Value靜態(tài)函數(shù)和Value的實(shí)例函數(shù)。
參考
https://golang.org/pkg/reflect/https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/
Wenguang Liu:Golang中Routine閉包中的一個(gè)坑?zhuanlan.zhihu.com總結(jié)
以上是生活随笔為你收集整理的golang 结构体断言_Golang中的reflect原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机图形学 dda,计算机图形学直线D
- 下一篇: 直线段的矢栅转换算法(DDA算法、中心画