【導(dǎo)讀】golang反射和接口是如何工作?使用反射有什么注意點?本文對go反射做了詳細介紹。
反射用法
反射定律
從接口值到反射對象的反射
反射是一種檢查存儲在接口變量中的(類型,值)對的機制。作為一個開始,我們需要知道reflect包中的兩個類型:Type和Value。這兩種類型給了我們訪問一個接口變量中所包含的內(nèi)容的途徑,另外兩個簡單的函數(shù)reflect.Typeof和reflect.Valueof可以檢索一個接口值的reflect.Type和reflect.Value部分。
package main
import (
“fmt”
“reflect”
)
func main() {
var x float64 = 3.4
fmt.Println(“type:”, reflect.TypeOf(x))
}
reflect.Typeof 簽名里就包含了一個空接口:
func TypeOf(i interface{}) Type
當我們調(diào)用reflect.Typeof(x)的時候,x首先被保存到一個空接口中,這個空接口然后被作為參數(shù)傳遞。reflect.Typeof 會把這個空接口拆包(unpack)恢復(fù)出類型信息。
當然,reflect.Valueof可以把值恢復(fù)出來
var x float64 = 3.4
fmt.Println(“value:”, reflect.ValueOf(x))//Valueof方法會返回一個Value類型的對象
reflect.Type和reflect.Value這兩種類型都提供了大量的方法讓我們可以檢查和操作這兩種類型。一個重要的例子是:
Value類型有一個 Type 方法可以返回reflect.Value類型的Type(這個方法返回的是值的靜態(tài)類型即static type,也就是說如果定義了type MyInt int64,那么這個函數(shù)返回的是MyInt類型而不是int64
Type 和 Value 都有一個Kind方法可以返回一個常量用于指示一個項到底是以什么形式(也就是底層類型即underlying type,繼續(xù)前面括號里提到的,Kind返回的是int64而不是MyInt)存儲的,這些常量包括:Unit, Float64, Slice等等。而且,有關(guān)Value類型的帶有名字諸如Int和Float的方法可讓讓我們獲取存在里面的值(比如int64和float64):
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println(“type:”, v.Type())
fmt.Println(“kind is float64:”, v.Kind() == reflect.Float64)
fmt.Println(“value:”, v.Float())
type: float64
kind is float64: true
value: 3.4
反射庫里有倆性質(zhì)值得單獨拿出來說說。第一個性質(zhì)是,為了保持API簡單,Value的”setter”和“getter”類型的方法操作的是可以包含某個值的最大類型:比如,所有的有符號整型,只有針對int64類型的方法,因為它是所有的有符號整型中最大的一個類型。也就是說,Value的Int方法返回的是一個int64,同時SetInt的參數(shù)類型采用的是一個int64;所以,必要時要轉(zhuǎn)換成實際類型:
var x uint8 = ‘x’
v := reflect.ValueOf(x)
fmt.Println(“type:”, v.Type()) // uint8.
fmt.Println(“kind is uint8: ”, v.Kind() == reflect.Uint8) // true.
x = uint8(v.Uint())// v.Uint returns a uint64.看到啦嘛?這個地方必須進行強制類型轉(zhuǎn)換!
第二個性質(zhì)是,反射對象(reflection object)的Kind描述的是底層類型(underlying type)
從反射隊形到接口值的反射
就像物理學(xué)上的反射,Go中到反射可以生成它的逆。
給定一個reflect.Value,我們能用Interface方法把它恢復(fù)成一個接口值;效果上就是這個Interface方法把類型和值的信息打包成一個接口表示并且返回結(jié)果:
func (v Value) Interface() interface{}
y := v.Interface()。(float64) // y will have type float64.
fmt.Println(y)
我們甚至可以做得更好一些,fmt.Println等方法的參數(shù)是一個空接口類型的值,所以我們可以讓fmt包自己在內(nèi)部完成我們在上面代碼中做的工作。因此,為了正確打印一個reflect.Value,我們只需把Interface方法的返回值直接傳遞給這個格式化輸出例程:
fmt.Println(v.Interface())
fmt.Printf(“value is %7.1e
”, v.Interface())
3.4e+00
還有就是,我們不需要對v.Interface方法的結(jié)果調(diào)用類型斷言(type-assert)為float64;空接口類型值內(nèi)部包含有具體值的類型信息,并且Printf方法會把它恢復(fù)出來。
簡要的說,Interface方法是Valueof函數(shù)的逆,除了它的返回值的類型總是interface{}靜態(tài)類型。
為了修改一個反射對象,值必須是settable的
下面是一些不能正常運行的代碼,但是很值得研究:
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
問題不是出在值7.1不是可以尋址的,而是出在v不是settable的。Settability是Value的一條性質(zhì),而且,不是所有的Value都具備這條性質(zhì)。
Value的CanSet方法用與測試一個Value的settablity;在我們的例子中,
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println(“settability of v:”, v.CanSet())
settability of v: false
如果對一個non-settable的Value調(diào)用Set方法會出現(xiàn)錯誤。但是,settability到底是什么呢?
settability有點像addressability,但是更加嚴格。
settability是一個性質(zhì),描述的是一個反射對象能夠修改創(chuàng)造它的那個實際存儲的值的能力。settability由反射對象是否保存原始項(original item)而決定。
var x float64 = 3.4
v := reflect.ValueOf(x)
我們傳遞了x的一個副本給reflect.Valueof函數(shù),所以作為reflect.Valueof參數(shù)被創(chuàng)造出來的接口值只是x的一個副本,而不是x本身。
因為,如果下面這條語句
v.SetFloat(7.1)
執(zhí)行成功(當然不可能執(zhí)行成功啦,假設(shè)而已),它不會更新x,即使v看起來像是從x創(chuàng)造而來,所以它更新的只是存儲在反射值內(nèi)部的x的一個副本,而x本身不受絲毫影響,所以如果真這樣的話,將會非常那令人困惑,而且一點用都沒有!所以,這么干是非法的,而settability就是用來阻止這種哦給你非法狀況出現(xiàn)的。
如果我們想通過反射來修改x,我們必須把我們想要修改的值的指針傳給一個反射庫。
首先,我們像平常一樣初始化x,然后創(chuàng)造一個指向它的反射值,叫做p.
var x float64 = 3.4
p := reflect.ValueOf(&x) // Note: take the address of x.注意這里哦!我們把x地址傳進去了!
fmt.Println(“type of p:”, p.Type())
fmt.Println(“settability of p:”, p.CanSet())
type of p: *float64
settability of p: false
反射對象p不是settable的,但是我們想要設(shè)置的不是p,而是(效果上來說)*p。為了得到p指向的東西,我們調(diào)用Value的Elem方法,這樣就能迂回繞過指針,同時把結(jié)果保存在叫v的Value中:
v := p.Elem()
fmt.Println(“settability of v:”, v.CanSet())
settability of v: true
現(xiàn)在v就是一個settable的反射對象了,并且因為v表示x,我們最終能夠通過v.SetFloat方法來修改x的值:
v.SetFloat(7.1)
fmt.Println(v.Interface())
fmt.Println(x)
輸出正是我們所期待的,反射理解起來有點困難,但是它確實正在做編程語言要做的,盡管是通過掩蓋了所發(fā)生的一切的反射Types和Vlues來實現(xiàn)的。這樣好了,你就直接記住反射Values為了修改它們所表示的東西必須要有這些東西的地址。
type 的方法集
來源 :Golang學(xué)習(xí) - reflect 包https://www.cnblogs.com/golove/p/5909541.html
type Type interface {
// Methods applicable to all types.
// 獲取 t 類型的值在分配內(nèi)存時的字節(jié)對齊值。
Align() int
// 獲取 t 類型的值作為結(jié)構(gòu)體字段時的字節(jié)對齊值。
FieldAlign() int
// 根據(jù)索引獲取 t 類型的方法,如果方法不存在,則 panic。
// 如果 t 是一個實際的類型,則返回值的 Type 和 Func 字段會列出接收者。
// 如果 t 只是一個接口,則返回值的 Type 不列出接收者,F(xiàn)unc 為空值。
Method(int) Method
// 根據(jù)名稱獲取 t 類型的方法。
MethodByName(string) (Method, bool)
// 獲取 t 類型的方法數(shù)量。
NumMethod() int
// 獲取 t 類型在其包中定義的名稱,未命名類型則返回空字符串。
Name() string
// 獲取 t 類型所在包的名稱,未命名類型則返回空字符串。
PkgPath() string
// 獲取 t 類型的值在分配內(nèi)存時的大小,功能和 unsafe.SizeOf 一樣。
Size() uintptr
// 獲取 t 類型的字符串描述,不要通過 String 來判斷兩種類型是否一致。
String() string
// 獲取 t 類型的類別。
Kind() Kind
// 判斷 t 類型是否實現(xiàn)了 u 接口。
Implements(u Type) bool
// 判斷 t 類型的值可否賦值給 u 類型。
AssignableTo(u Type) bool
// 判斷 t 類型的值可否轉(zhuǎn)換為 u 類型。
ConvertibleTo(u Type) bool
// 判斷 t 類型的值可否進行比較操作
Comparable() bool
// Methods applicable only to some types, depending on Kind.
// 特定類型的函數(shù):
//
// 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
// 獲取數(shù)值類型的位寬,t 必須是整型、浮點型、復(fù)數(shù)型
Bits() int
// 獲取通道的方向
ChanDir() ChanDir
// For concreteness, if t represents func(x int, y 。.. float64), then
//
// t.NumIn() == 2
// t.In(0) is the reflect.Type for “int”
// t.In(1) is the reflect.Type for “[]float64”
// t.IsVariadic() == true
// 判斷函數(shù)是否具有可變參數(shù)。
// 如果有可變參數(shù),則 t.In(t.NumIn()-1) 將返回一個切片。
IsVariadic() bool
// 數(shù)組、切片、映射、通道、指針、接口
// 獲取元素類型、獲取指針所指對象類型,獲取接口的動態(tài)類型
Elem() Type
// 根據(jù)索引獲取字段
Field(i int) StructField
// 根據(jù)索引鏈獲取嵌套字段
FieldByIndex(index []int) StructField
// 根據(jù)名稱獲取字段
FieldByName(name string) (StructField, bool)
// 根據(jù)指定的匹配函數(shù) math 獲取字段
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 根據(jù)索引獲取函數(shù)的參數(shù)信息
In(i int) Type
// Key returns a map type‘s key type.
// It panics if the type’s Kind is not Map.
Key() Type
// Len returns an array type‘s length.
// It panics if the type’s Kind is not Array.
Len() int
// 獲取字段數(shù)量
NumField() int
// 獲取函數(shù)的參數(shù)數(shù)量
NumIn() int
// 獲取函數(shù)的返回值數(shù)量
NumOut() int
// 根據(jù)索引獲取函數(shù)的返回值信息
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
value 方法集
// 特殊
// 判斷 v 值是否可尋址
// 1、指針的 Elem() 可尋址
// 2、切片的元素可尋址
// 3、可尋址數(shù)組的元素可尋址
// 4、可尋址結(jié)構(gòu)體的字段可尋址,方法不可尋址
// 也就是說,如果 v 值是指向數(shù)組的指針“&數(shù)組”,通過 v.Elem() 獲取該指針指向的數(shù)組,那么
// 該數(shù)組就是可尋址的,同時該數(shù)組的元素也是可尋址的,如果 v 就是一個普通數(shù)組,不是通過解引
// 用得到的數(shù)組,那么該數(shù)組就不可尋址,其元素也不可尋址。結(jié)構(gòu)體亦然。
func (v Value) CanAddr() bool
// 獲取 v 值的地址,相當于 & 取地址操作。v 值必須可尋址。
func (v Value) Addr() reflect.Value
// 判斷 v 值是否可以被修改。只有可尋址的 v 值可被修改。
// 結(jié)構(gòu)體中的非導(dǎo)出字段(通過 Field() 等方法獲取的)不能修改,所有方法不能修改。
func (v Value) CanSet() bool
// 判斷 v 值是否可以轉(zhuǎn)換為接口類型
// 結(jié)構(gòu)體中的非導(dǎo)出字段(通過 Field() 等方法獲取的)不能轉(zhuǎn)換為接口類型
func (v Value) CanInterface() bool
// 將 v 值轉(zhuǎn)換為空接口類型。v 值必須可轉(zhuǎn)換為接口類型。
func (v Value) Interface() interface{}
// 使用一對 uintptr 返回接口的數(shù)據(jù)
func (v Value) InterfaceData() [2]uintptr
// 指針
// 將 v 值轉(zhuǎn)換為 uintptr 類型,v 值必須是切片、映射、通道、函數(shù)、指針、自由指針。
func (v Value) Pointer() uintptr
// 獲取 v 值的地址。v 值必須是可尋址類型(CanAddr)。
func (v Value) UnsafeAddr() uintptr
// 將 UnsafePointer 類別的 v 值修改為 x,v 值必須是 UnsafePointer 類別,必須可修改。
func (v Value) SetPointer(x unsafe.Pointer)
// 判斷 v 值是否為 nil,v 值必須是切片、映射、通道、函數(shù)、接口、指針。
// IsNil 并不總等價于 Go 的潛在比較規(guī)則,比如對于 var i interface{},i == nil 將返回
// true,但是 reflect.ValueOf(i).IsNil() 將 panic。
func (v Value) IsNil() bool
// 獲取“指針所指的對象”或“接口所包含的對象”
func (v Value) Elem() reflect.Value
// 通用
// 獲取 v 值的字符串描述
func (v Value) String() string
// 獲取 v 值的類型
func (v Value) Type() reflect.Type
// 返回 v 值的類別,如果 v 是空值,則返回 reflect.Invalid。
func (v Value) Kind() reflect.Kind
// 獲取 v 的方法數(shù)量
func (v Value) NumMethod() int
// 根據(jù)索引獲取 v 值的方法,方法必須存在,否則 panic
// 使用 Call 調(diào)用方法的時候不用傳入接收者,Go 會自動把 v 作為接收者傳入。
func (v Value) Method(int) reflect.Value
// 根據(jù)名稱獲取 v 值的方法,如果該方法不存在,則返回空值(reflect.Invalid)。
func (v Value) MethodByName(string) reflect.Value
// 判斷 v 本身(不是 v 值)是否為零值。
// 如果 v 本身是零值,則除了 String 之外的其它所有方法都會 panic。
func (v Value) IsValid() bool
// 將 v 值轉(zhuǎn)換為 t 類型,v 值必須可轉(zhuǎn)換為 t 類型,否則 panic。
func (v Value) Convert(t Type) reflect.Value
// 獲取
// 獲取 v 值的內(nèi)容,如果 v 值不是有符號整型,則 panic。
func (v Value) Int() int64
// 獲取 v 值的內(nèi)容,如果 v 值不是無符號整型(包括 uintptr),則 panic。
func (v Value) Uint() uint64
// 獲取 v 值的內(nèi)容,如果 v 值不是浮點型,則 panic。
func (v Value) Float() float64
// 獲取 v 值的內(nèi)容,如果 v 值不是復(fù)數(shù)型,則 panic。
func (v Value) Complex() complex128
// 獲取 v 值的內(nèi)容,如果 v 值不是布爾型,則 panic。
func (v Value) Bool() bool
// 獲取 v 值的長度,v 值必須是字符串、數(shù)組、切片、映射、通道。
func (v Value) Len() int
// 獲取 v 值的容量,v 值必須是數(shù)值、切片、通道。
func (v Value) Cap() int
// 獲取 v 值的第 i 個元素,v 值必須是字符串、數(shù)組、切片,i 不能超出范圍。
func (v Value) Index(i int) reflect.Value
// 獲取 v 值的內(nèi)容,如果 v 值不是字節(jié)切片,則 panic。
func (v Value) Bytes() []byte
// 獲取 v 值的切片,切片長度 = j - i,切片容量 = v.Cap() - i。
// v 必須是字符串、數(shù)值、切片,如果是數(shù)組則必須可尋址。i 不能超出范圍。
func (v Value) Slice(i, j int) reflect.Value
// 獲取 v 值的切片,切片長度 = j - i,切片容量 = k - i。
// i、j、k 不能超出 v 的容量。i 《= j 《= k。
// v 必須是字符串、數(shù)值、切片,如果是數(shù)組則必須可尋址。i 不能超出范圍。
func (v Value) Slice3(i, j, k int) reflect.Value
// 根據(jù) key 鍵獲取 v 值的內(nèi)容,v 值必須是映射。
// 如果指定的元素不存在,或 v 值是未初始化的映射,則返回零值(reflect.ValueOf(nil))
func (v Value) MapIndex(key Value) reflect.Value
// 獲取 v 值的所有鍵的無序列表,v 值必須是映射。
// 如果 v 值是未初始化的映射,則返回空列表。
func (v Value) MapKeys() []reflect.Value
// 判斷 x 是否超出 v 值的取值范圍,v 值必須是有符號整型。
func (v Value) OverflowInt(x int64) bool
// 判斷 x 是否超出 v 值的取值范圍,v 值必須是無符號整型。
func (v Value) OverflowUint(x uint64) bool
// 判斷 x 是否超出 v 值的取值范圍,v 值必須是浮點型。
func (v Value) OverflowFloat(x float64) bool
// 判斷 x 是否超出 v 值的取值范圍,v 值必須是復(fù)數(shù)型。
func (v Value) OverflowComplex(x complex128) bool
------------------------------
// 設(shè)置(這些方法要求 v 值必須可修改)
// 設(shè)置 v 值的內(nèi)容,v 值必須是有符號整型。
func (v Value) SetInt(x int64)
// 設(shè)置 v 值的內(nèi)容,v 值必須是無符號整型。
func (v Value) SetUint(x uint64)
// 設(shè)置 v 值的內(nèi)容,v 值必須是浮點型。
func (v Value) SetFloat(x float64)
// 設(shè)置 v 值的內(nèi)容,v 值必須是復(fù)數(shù)型。
func (v Value) SetComplex(x complex128)
// 設(shè)置 v 值的內(nèi)容,v 值必須是布爾型。
func (v Value) SetBool(x bool)
// 設(shè)置 v 值的內(nèi)容,v 值必須是字符串。
func (v Value) SetString(x string)
// 設(shè)置 v 值的長度,v 值必須是切片,n 不能超出范圍,不能為負數(shù)。
func (v Value) SetLen(n int)
// 設(shè)置 v 值的內(nèi)容,v 值必須是切片,n 不能超出范圍,不能小于 Len。
func (v Value) SetCap(n int)
// 設(shè)置 v 值的內(nèi)容,v 值必須是字節(jié)切片。x 可以超出 v 值容量。
func (v Value) SetBytes(x []byte)
// 設(shè)置 v 值的鍵和值,如果鍵存在,則修改其值,如果鍵不存在,則添加鍵和值。
// 如果將 val 設(shè)置為零值(reflect.ValueOf(nil)),則刪除該鍵。
// 如果 v 值是一個未初始化的 map,則 panic。
func (v Value) SetMapIndex(key, val reflect.Value)
// 設(shè)置 v 值的內(nèi)容,v 值必須可修改,x 必須可以賦值給 v 值。
func (v Value) Set(x reflect.Value)
------------------------------
// 結(jié)構(gòu)體
// 獲取 v 值的字段數(shù)量,v 值必須是結(jié)構(gòu)體。
func (v Value) NumField() int
// 根據(jù)索引獲取 v 值的字段,v 值必須是結(jié)構(gòu)體。如果字段不存在則 panic。
func (v Value) Field(i int) reflect.Value
// 根據(jù)索引鏈獲取 v 值的嵌套字段,v 值必須是結(jié)構(gòu)體。
func (v Value) FieldByIndex(index []int) reflect.Value
// 根據(jù)名稱獲取 v 值的字段,v 值必須是結(jié)構(gòu)體。
// 如果指定的字段不存在,則返回零值(reflect.ValueOf(nil))
func (v Value) FieldByName(string) reflect.Value
// 根據(jù)匹配函數(shù) match 獲取 v 值的字段,v 值必須是結(jié)構(gòu)體。
// 如果沒有匹配的字段,則返回零值(reflect.ValueOf(nil))
func (v Value) FieldByNameFunc(match func(string) bool) Value
// 函數(shù)
// 通過參數(shù)列表 in 調(diào)用 v 值所代表的函數(shù)(或方法)。函數(shù)的返回值存入 r 中返回。
// 要傳入多少參數(shù)就在 in 中存入多少元素。
// Call 即可以調(diào)用定參函數(shù)(參數(shù)數(shù)量固定),也可以調(diào)用變參函數(shù)(參數(shù)數(shù)量可變)。
func (v Value) Call(in []Value) (r []Value)
// 通過參數(shù)列表 in 調(diào)用 v 值所代表的函數(shù)(或方法)。函數(shù)的返回值存入 r 中返回。
// 函數(shù)指定了多少參數(shù)就在 in 中存入多少元素,變參作為一個單獨的參數(shù)提供。
// CallSlice 只能調(diào)用變參函數(shù)。
func (v Value) CallSlice(in []Value) []Value
// 通道
// 發(fā)送數(shù)據(jù)(會阻塞),v 值必須是可寫通道。
func (v Value) Send(x reflect.Value)
// 接收數(shù)據(jù)(會阻塞),v 值必須是可讀通道。
func (v Value) Recv() (x reflect.Value, ok bool)
// 嘗試發(fā)送數(shù)據(jù)(不會阻塞),v 值必須是可寫通道。
func (v Value) TrySend(x reflect.Value) bool
// 嘗試接收數(shù)據(jù)(不會阻塞),v 值必須是可讀通道。
func (v Value) TryRecv() (x reflect.Value, ok bool)
// 關(guān)閉通道,v 值必須是通道。
func (v Value) Close()
// 示例
var f1 = func(a int, b []int) { fmt.Println(a, b) }
var f2 = func(a int, b 。..int) { fmt.Println(a, b) }
func main() {
v1 := reflect.ValueOf(f1)
v2 := reflect.ValueOf(f2)
a := reflect.ValueOf(1)
b := reflect.ValueOf([]int{1, 2, 3})
v1.Call([]reflect.Value{a, b})
v2.Call([]reflect.Value{a, a, a, a, a, a})
//v1.CallSlice([]reflect.Value{a, b}) // 非變參函數(shù),不能用 CallSlice。
v2.CallSlice([]reflect.Value{a, b})
}
樣例
類型的字段標識
下面是分析一個struct值,t,的簡單例子。我們用這個struct的地址創(chuàng)建一個反射對象,因為我們想一會改變它的值。然后我們把typeofT變量設(shè)置為這個反射對象的類型,接著使用一些直接的方法調(diào)用(細節(jié)請見reflect包)來迭代各個域。注意,我們從struct類型中提取了各個域的名字,但是這些域本身都是reflect.Value對象。
type T struct {
A int
B string
}
t := T{23, “skidoo”}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type()//把s.Type()返回的Type對象復(fù)制給typeofT,typeofT也是一個反射。
for i := 0; i 《 s.NumField(); i++ {
f := s.Field(i)//迭代s的各個域,注意每個域仍然是反射。
fmt.Printf(“%d: %s %s = %v
”, i,
typeOfT.Field(i).Name, f.Type(), f.Interface())//提取了每個域的名字
}
0: A int = 23
1: B string = skidoo
reflect.Type的Field方法將返回一個reflect.StructField,里面含有每個成員的名字、類型和可選的成員標簽等信息。
因為s包含了一個settable的反射對象,所以我們可以修改這個structure的各個域。
s.Field(0).SetInt(77)
s.Field(1).SetString(“Sunset Strip”)
fmt.Println(“t is now”, t)
t is now {77 Sunset Strip}
類型的方法集
func Print(x interface{}) {
v := reflect.ValueOf(x)
t := v.Type()
fmt.Printf(“type %s
”, t)
for i := 0; i 《 v.NumMethod(); i++ {
methType := v.Method(i).Type()
fmt.Printf(“func (%s) %s%s
”, t, t.Method(i).Name,
strings.TrimPrefix(methType.String(), “func”))
}
}
reflect.Type和reflect.Value都提供了一個Method方法。每次t.Method(i)調(diào)用將一個reflect.Method的實例,對應(yīng)一個用于描述一個方法的名稱和類型的結(jié)構(gòu)體。每次v.Method(i)方法調(diào)用都返回一個reflect.Value以表示對應(yīng)的值(§6.4),也就是一個方法是幫到它的接收者的。使用reflect.Value.Call方法(我們之類沒有演示),將可以調(diào)用一個Func類型的Value,但是這個例子中只用到了它的類型。
methods.Print(time.Hour)
// Output:
// type time.Duration
// func (time.Duration) Hours() float64
// func (time.Duration) Minutes() float64
// func (time.Duration) Nanoseconds() int64
// func (time.Duration) Seconds() float64
// func (time.Duration) String() string
methods.Print(new(strings.Replacer))
// Output:
// type *strings.Replacer
// func (*strings.Replacer) Replace(string) string
// func (*strings.Replacer) WriteString(io.Writer, string) (int, error)
反射的原理
Typeof
Typeof 函數(shù)非常簡單,在調(diào)用 Typeof 函數(shù)的時候,變量就已經(jīng)被轉(zhuǎn)化為 interface 類型,Typeof 只需要將它的 typ 屬性取出來即可。
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
}
type.Name 函數(shù)
解析類型的名稱是一個反射很基礎(chǔ)的功能,它和 String 方法的不同在于,它不會包含類型所在包的名字,例如 main.Cat 與 Cat,所以一定不要用 name 來區(qū)分類型。
從實現(xiàn)來看,Name 是建立在 String 函數(shù)的基礎(chǔ)上的,它找到了 。 這個字符然后分割了字符串。
從下面的代碼中可以看到,rtype 的 str(nameoff) 屬性并不是簡單的距離,而是距離各個模塊 types 的距離。
func (t *rtype) Name() string {
if t.tflag&tflagNamed == 0 {
return “”
}
s := t.String()
i := len(s) - 1
for i 》= 0 && s[i] != ‘?!?{
i--
}
return s[i+1:]
}
func (t *rtype) String() string {
s := t.nameOff(t.str).name()
if t.tflag&tflagExtraStar != 0 {
return s[1:]
}
return s
}
func (t *rtype) nameOff(off nameOff) name {
return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))}
}
// reflect_resolveNameOff resolves a name offset from a base pointer.
//go:linkname reflect_resolveNameOff reflect.resolveNameOff
func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer {
return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes)
}
func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
if off == 0 {
return name{}
}
base := uintptr(ptrInModule)
for md := &firstmoduledata; md != nil; md = md.next {
if base 》= md.types && base 《 md.etypes {
res := md.types + uintptr(off)
if res 》 md.etypes {
println(“runtime: nameOff”, hex(off), “out of range”, hex(md.types), “-”, hex(md.etypes))
throw(“runtime: name offset out of range”)
}
return name{(*byte)(unsafe.Pointer(res))}
}
}
// No module found. see if it is a run time name.
reflectOffsLock()
res, found := reflectOffs.m[int32(off)]
reflectOffsUnlock()
if !found {
println(“runtime: nameOff”, hex(off), “base”, hex(base), “not in ranges:”)
for next := &firstmoduledata; next != nil; next = next.next {
println(“ types”, hex(next.types), “etypes”, hex(next.etypes))
}
throw(“runtime: name offset base pointer out of range”)
}
return name{(*byte)(res)}
}
type.Field
func (t *rtype) Field(i int) StructField {
if t.Kind() != Struct {
panic(“reflect: Field of non-struct type”)
}
tt := (*structType)(unsafe.Pointer(t))
return tt.Field(i)
}
func (t *structType) Field(i int) (f StructField) {
if i 《 0 || i 》= len(t.fields) {
panic(“reflect: Field index out of bounds”)
}
p := &t.fields[i]
f.Type = toType(p.typ)
f.Name = p.name.name()
f.Anonymous = p.embedded()
if !p.name.isExported() {
f.PkgPath = t.pkgPath.name()
}
if tag := p.name.tag(); tag != “” {
f.Tag = StructTag(tag)
}
f.Offset = p.offset()
// NOTE(rsc): This is the only allocation in the interface
// presented by a reflect.Type. It would be nice to avoid,
// at least in the common cases, but we need to make sure
// that misbehaving clients of reflect cannot affect other
// uses of reflect. One possibility is CL 5371098, but we
// postponed that ugliness until there is a demonstrated
// need for the performance. This is issue 2320.
f.Index = []int{i}
return
}
type.Method 方法
對于 golang 里面的類型,它們的方法都是存儲在 uncommon 的部分當中,而且他們的數(shù)據(jù)結(jié)構(gòu)是:
type method struct {
name nameOff // name of method
mtyp typeOff // method type (without receiver)
ifn textOff // fn used in interface call (one-word receiver)
tfn textOff // fn used for normal method call
}
數(shù)據(jù)結(jié)構(gòu)中,mtyp 是 method 類型的地址,ifn 是接口函數(shù)的地址,tfn 是普通函數(shù)的地址。
它會被 Method 函數(shù)轉(zhuǎn)換為 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
}
Method 的 Type 由 mtyp 而來,F(xiàn)unc 由 tfn/ifn 而來,而 Func 是 Value 類型,F(xiàn)unc.typ 還是 mtyp,ptr 是 tfn/ifn。
func (t *rtype) Method(i int) (m Method) {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.Method(i)
}
methods := t.exportedMethods()
if i 《 0 || i 》= len(methods) {
panic(“reflect: Method index out of range”)
}
p := methods[i]
pname := t.nameOff(p.name)
m.Name = pname.name()
fl := flag(Func)
mtyp := t.typeOff(p.mtyp)
ft := (*funcType)(unsafe.Pointer(mtyp))
in := make([]Type, 0, 1+len(ft.in()))
in = append(in, t)
for _, arg := range ft.in() {
in = append(in, arg)
}
out := make([]Type, 0, len(ft.out()))
for _, ret := range ft.out() {
out = append(out, ret)
}
mt := FuncOf(in, out, ft.IsVariadic())
m.Type = mt
tfn := t.textOff(p.tfn)
fn := unsafe.Pointer(&tfn)
m.Func = Value{mt.(*rtype), fn, fl}
m.Index = i
return m
}
func (t *rtype) exportedMethods() []method {
ut := t.uncommon()
if ut == nil {
return nil
}
return ut.exportedMethods()
}
func (t *uncommonType) exportedMethods() []method {
if t.xcount == 0 {
return nil
}
return (*[1 《《 16]method)(add(unsafe.Pointer(t), uintptr(t.moff), “t.xcount 》 0”))[t.xcount]
}
ValueOf
func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// TODO: Maybe allow contents of a Value to live on the stack.
// For now we make the contents always escape to the heap. It
// makes life easier in a few places (see chanrecv/mapassign
// comment below)。
escapes(i)
return unpackEface(i)
}
func unpackEface(i interface{}) Value {
e := (*emptyInterface)(unsafe.Pointer(&i))
// NOTE: don‘t read e.word until we know whether it is really a pointer or not.
t := e.typ
if t == nil {
return Value{}
}
f := flag(t.Kind())
if ifaceIndir(t) {
f |= flagIndir
}
return Value{t, e.word, f}
}
value.Field
通過 value 的 Field 可以獲取到結(jié)構(gòu)體的內(nèi)部屬性值,結(jié)構(gòu)體的內(nèi)部屬性都是 structField 類型的,每個 structField.offsetEmbed 是該屬性值距離結(jié)構(gòu)體地址的偏移量。
func (v Value) Field(i int) Value {
if v.kind() != Struct {
panic(&ValueError{“reflect.Value.Field”, v.kind()})
}
tt := (*structType)(unsafe.Pointer(v.typ))
if uint(i) 》= uint(len(tt.fields)) {
panic(“reflect: Field index out of range”)
}
field := &tt.fields[i]
typ := field.typ
// Inherit permission bits from v, but clear flagEmbedRO.
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
// Using an unexported field forces flagRO.
if !field.name.isExported() {
if field.embedded() {
fl |= flagEmbedRO
} else {
fl |= flagStickyRO
}
}
// Either flagIndir is set and v.ptr points at struct,
// or flagIndir is not set and v.ptr is the actual struct data.
// In the former case, we want v.ptr + offset.
// In the latter case, we must have field.offset = 0,
// so v.ptr + field.offset is still the correct address.
ptr := add(v.ptr, field.offset(), “same as non-reflect &v.field”)
return Value{typ, ptr, fl}
}
type structField struct {
name name // name is always non-empty
typ *rtype // type of field
offsetEmbed uintptr // byte offset of field《《1 | isEmbedded
}
func (f *structField) offset() uintptr {
return f.offsetEmbed 》》 1
}
value.Method
我們從下面的代碼中可以看到,Method 也是返回一個 Value,但是這個 Value 的 ptr 并不是第 i 個函數(shù)的地址,而是原封不動的將原 value 的 ptr 返回了,僅僅是對 flag 設(shè)置比特位而已。
func (v Value) Method(i int) Value {
if v.typ == nil {
panic(&ValueError{“reflect.Value.Method”, Invalid})
}
if v.flag&flagMethod != 0 || uint(i) 》= uint(v.typ.NumMethod()) {
panic(“reflect: Method index out of range”)
}
if v.typ.Kind() == Interface && v.IsNil() {
panic(“reflect: Method on nil interface value”)
}
fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
fl |= flag(Func)
fl |= flag(i)《《flagMethodShift | flagMethod
return Value{v.typ, v.ptr, fl}
}
value.Call
Call 函數(shù)目的是調(diào)用 value 的相應(yīng)的函數(shù),這里和 Method 是相互呼應(yīng)的,使用了 flag 的 flagMethodShift,得到了相應(yīng)的函數(shù)地址。
func (v Value) Call(in []Value) []Value {
v.mustBe(Func)
v.mustBeExported()
return v.call(“Call”, in)
}
func (v Value) call(op string, in []Value) []Value {
// Get function pointer, type.
t := (*funcType)(unsafe.Pointer(v.typ))
var (
fn unsafe.Pointer
rcvr Value
rcvrtype *rtype
)
if v.flag&flagMethod != 0 {
rcvr = v
rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)》》flagMethodShift)
} else if v.flag&flagIndir != 0 {
fn = *(*unsafe.Pointer)(v.ptr)
} else {
fn = v.ptr
}
if fn == nil {
panic(“reflect.Value.Call: call of nil function”)
}
isSlice := op == “CallSlice”
n := t.NumIn()
if isSlice {
if !t.IsVariadic() {
panic(“reflect: CallSlice of non-variadic function”)
}
if len(in) 《 n {
panic(“reflect: CallSlice with too few input arguments”)
}
if len(in) 》 n {
panic(“reflect: CallSlice with too many input arguments”)
}
} else {
if t.IsVariadic() {
n--
}
if len(in) 《 n {
panic(“reflect: Call with too few input arguments”)
}
if !t.IsVariadic() && len(in) 》 n {
panic(“reflect: Call with too many input arguments”)
}
}
for _, x := range in {
if x.Kind() == Invalid {
panic(“reflect: ” + op + “ using zero Value argument”)
}
}
for i := 0; i 《 n; i++ {
if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) {
panic(“reflect: ” + op + “ using ” + xt.String() + “ as type ” + targ.String())
}
}
if !isSlice && t.IsVariadic() {
// prepare slice for remaining values
m := len(in) - n
slice := MakeSlice(t.In(n), m, m)
elem := t.In(n).Elem()
for i := 0; i 《 m; i++ {
x := in[n+i]
if xt := x.Type(); !xt.AssignableTo(elem) {
panic(“reflect: cannot use ” + xt.String() + “ as type ” + elem.String() + “ in ” + op)
}
slice.Index(i).Set(x)
}
origIn := in
in = make([]Value, n+1)
copy(in[:n], origIn)
in[n] = slice
}
nin := len(in)
if nin != t.NumIn() {
panic(“reflect.Value.Call: wrong argument count”)
}
nout := t.NumOut()
// Compute frame type.
frametype, _, retOffset, _, framePool := funcLayout(t, rcvrtype)
// Allocate a chunk of memory for frame.
var args unsafe.Pointer
if nout == 0 {
args = framePool.Get()。(unsafe.Pointer)
} else {
// Can’t use pool if the function has return values.
// We will leak pointer to args in ret, so its lifetime is not scoped.
args = unsafe_New(frametype)
}
off := uintptr(0)
// Copy inputs into args.
if rcvrtype != nil {
storeRcvr(rcvr, args)
off = ptrSize
}
for i, v := range in {
v.mustBeExported()
targ := t.In(i)。(*rtype)
a := uintptr(targ.align)
off = (off + a - 1) &^ (a - 1)
n := targ.size
if n == 0 {
// Not safe to compute args+off pointing at 0 bytes,
// because that might point beyond the end of the frame,
// but we still need to call assignTo to check assignability.
v.assignTo(“reflect.Value.Call”, targ, nil)
continue
}
addr := add(args, off, “n 》 0”)
v = v.assignTo(“reflect.Value.Call”, targ, addr)
if v.flag&flagIndir != 0 {
typedmemmove(targ, addr, v.ptr)
} else {
*(*unsafe.Pointer)(addr) = v.ptr
}
off += n
}
// Call.
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset))
// For testing; see TestCallMethodJump.
if callGC {
runtime.GC()
}
var ret []Value
if nout == 0 {
typedmemclr(frametype, args)
framePool.Put(args)
} else {
// Zero the now unused input area of args,
// because the Values returned by this function contain pointers to the args object,
// and will thus keep the args object alive indefinitely.
typedmemclrpartial(frametype, args, 0, retOffset)
// Wrap Values around return values in args.
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}
// Note: this does introduce false sharing between results -
// if any result is live, they are all live.
// (And the space for the args is live as well, but as we‘ve
// cleared that space it isn’t as big a deal.)
} else {
// For zero-sized return value, args+off may point to the next object.
// In this case, return the zero value instead.
ret[i] = Zero(tv)
}
off += tv.Size()
}
}
return ret
}
原文標題:Go interface 反射
文章出處:【微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
責任編輯:haq
-
接口
+關(guān)注
關(guān)注
33文章
8448瀏覽量
150724 -
發(fā)射
+關(guān)注
關(guān)注
1文章
93瀏覽量
20839
原文標題:Go interface 反射
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論