您當前的位置:首頁 > 攝影

go語言反射探秘

作者:由 鄭天璣 發表于 攝影時間:2020-12-08

以下分為兩個部分講講go中的反射:

第一部分是核心要點

TypeOf

ValueOf

第二部分是應用反射功能寫一個簡單DI依賴注入容器

1

TypeOf

ValueOf

go語言反射探秘

TypeOf

返回

Type

介面,

ValueOf

返回

Value

結構體,此二者是後續所有操作的核心

TypeOf

reflect。TypeOf()

返回的是一個

Type

介面,定義了大多數對型別元資料的操作,比如

String()

是獲取型別名,

Kind()

返回具體型別的列舉值,

NumIn()

為傳入的是當函式型別時返回所有傳入引數的數量,同理

NumOut()

返回的是函式返回值的數量。

由此我們可以推測底層必然有一個

type結構體

實現了這個介面。

// Type介面原型: 我們需要終點關注的String() Elem() Kind()

type

Type

interface

{

// ……。

// String returns a string representation of the type。

// The string representation may use shortened package names

// (e。g。, base64 instead of “encoding/base64”) and is not

// guaranteed to be unique among types。 To test for type identity,

// compare the Types directly。

String

()

string

// Kind returns the specific kind of this type。

Kind

()

Kind

// Elem returns a type‘s element type。

// It panics if the type’s Kind is not Array, Chan, Map, Ptr, or Slice。

Elem

()

Type

// In returns the type of a function type‘s i’th input parameter。

// It panics if the type‘s Kind is not Func。

// It panics if i is not in the range [0, NumIn())。

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

// NumField returns a struct type’s field count。

// It panics if the type‘s Kind is not Struct。

NumField

()

int

// NumIn returns a function type’s input parameter count。

// It panics if the type‘s Kind is not Func。

NumIn

()

int

// NumOut returns a function type’s output parameter count。

// It panics if the type‘s Kind is not Func。

NumOut

()

int

// Implements reports whether the type implements the interface type u。

Implements

u

Type

bool

// ……。

}

假設我們在

package main

的包中定義了一個結構體:

type actorix struct

然後main方法中呼叫

reflect。TypeOf()

,然後列印其型別,會輸出

main。actorix

package

main

type

actorix

struct

{

id

int

name

string

number

float64

}

func

main

()

{

var

a

=

actorix

{

id

1

name

“this”

number

3。14

}

var

atype

=

reflect

TypeOf

a

fmt

Println

“typeof a is: ”

atype

String

())

// 列印結果:

// typeof a is: main。actorix

}

接下來我們具體看看

TypeOf

是怎麼工作的:

// TypeOf函式原型

func

TypeOf

i

interface

{})

Type

{

// 先把runtime。eface型別轉化為emptyInterface

// runtime。eface型別和emptyInterface型別本質是一樣的

// 這樣以來此時的emptyInterface。typ型別為*rtype

eface

:=

*

*

emptyInterface

)(

unsafe

Pointer

&

i

))

// 然後用toType()函式將emptyInterface。typ包裝成Type型別的返回值

// 到這裡我們可以明確rtype肯定實現了Type介面

return

toType

eface

typ

}

// emptyInterface結構原型

type

emptyInterface

struct

{

typ

*

rtype

// 指向的型別元資料

word

unsafe

Pointer

// 資料的指標

}

// eface結構體原型

type

eface

struct

{

_type

*

_type

// 指向的型別元資料

data

unsafe

Pointer

// 資料的指標

}

func

toType

t

*

rtype

Type

{

if

t

==

nil

{

return

nil

}

return

t

}

函式呼叫鏈如下:

TypeOf(interface{}) -> toType(*type)

接下來我們再來看看

rtype

結構體,可見它就是我們的型別元資料資訊,包括型別、大小、對其方式、雜湊值等,在type。go的原始碼中

rtype

實現了

Type

介面的方法,我們傳入的動態型別都指向了這個型別元資料

// 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

}

ValueOf

ValueOf

返回一個

Value

結構體,我們其實可以將其理解為:對我們傳入的變數進行了重新包裝,將它的資料指標、型別元資料、識別符號一併打包在一個結構體中返回,供我們後續使用:

type

Value

struct

{

typ

*

rtype

// 反射變數的型別元資料指標

ptr

unsafe

Pointer

// 資料地址

flag

// 位識別符號:描述資訊,是否為指標,是否為方法等,是否只讀等

}

// 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)。

// 官方再這個函式中已經明確說明將引數i逃逸到堆上

escapes

i

return

unpackEface

i

}

// unpackEface 函式本質上做了三件事情:

// 1轉換為將runtime。eface型別的i轉換為emptyInterface

// 2判明e。typ是否為空,如果為空返回一個空Value結構體

// 3如果不為空,根據e。typ的型別明確要返回的flag識別符號

// 最後將三者聯合在一起作為Value結構體返回

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

}

}

ValueOf

的函式原型中我們可以看到其傳入的函式引數是一個

interface{}

的空型別。

然而最後會將傳入的引數逃逸到堆上,所以我們要注意如下一個問題:

package

main

func

main

()

{

var

s

string

=

“this is sparta”

// 從編譯角度看,這裡可以分為兩步:

// 1 是臨時建立一個s的複製

// 2 s的複製作為引數傳入ValueOf後,被顯式地逃逸到了堆上

// 3 返回值中svalue指向的ptr,是堆上的那個s的地址,並非我們原先定義的s

var

svalue

=

reflect

ValueOf

s

// 4 當我們直接用SetString修改堆上的s複製沒有任何意義panic

svalue

SetString

“this is not sparta”

}

// 列印結果

// panic: reflect: reflect。Value。SetString using unaddressable value

// SetString函式原型

// SetString sets v‘s underlying value to x。

// It panics if v’s Kind is not String or if CanSet() is false。

func

v

Value

SetString

x

string

{

v

mustBeAssignable

()

// 確認必須可以被賦值

v

mustBe

String

// 確認必須是String型別

*

*

string

)(

v

ptr

=

x

// 直接指標操作賦值

}

這裡我們如果要完成原本操作需要藉助

Elem()

func

main

(){

var

s

string

=

“this is sparta”

// 注意這裡要傳遞s的指標

// 仍然氛圍兩部分引數複製了一個s的地址進入ValueOf

// 仍然將其逃逸到了堆上,此時svalue返回的是Value。ptr = 存放s地址的堆上的地址

// 比如s的地址為0x01,到了堆上,堆地址為:0xA,存放資料為0x01, Value。ptr = 0xA

// 相當於一個二級指標,因此直接SetString是沒有用的仍然是panic

// 需要透過Elem()函式 => 關鍵程式碼: ptr = *(*unsafe。Pointer)(ptr)

// 返回其指標指向的值,相當於二級指標解引用為一級指標

// 如此一來 svalue = svalue。Elem()後, svalue就是&s

// 我們可以對其進行SetString修改

var

svalue

=

reflect

ValueOf

&

s

svalue

=

svalue

Elem

()

svalue

SetString

“this is not sparta”

fmt

Println

s

}

// 列印結果:

// this is not sparta

// Elem函式原型如下

func

v

Value

Elem

()

Value

{

k

:=

v

kind

()

switch

k

{

case

Interface

var

eface

interface

{}

if

v

typ

NumMethod

()

==

0

{

eface

=

*

*

interface

{})(

v

ptr

}

else

{

eface

=

interface

{})(

*

*

interface

{

M

()})(

v

ptr

))

}

x

:=

unpackEface

eface

if

x

flag

!=

0

{

x

flag

|=

v

flag

ro

()

}

return

x

case

Ptr

ptr

:=

v

ptr

if

v

flag

&

flagIndir

!=

0

{

ptr

=

*

*

unsafe

Pointer

)(

ptr

}

// The returned value‘s address is v’s value。

if

ptr

==

nil

{

return

Value

{}

}

tt

:=

*

ptrType

)(

unsafe

Pointer

v

typ

))

typ

:=

tt

elem

fl

:=

v

flag

&

flagRO

|

flagIndir

|

flagAddr

fl

|=

flag

typ

Kind

())

return

Value

{

typ

ptr

fl

}

}

panic

&

ValueError

{

“reflect。Value。Elem”

v

kind

()})

}

如何驗證Elem()的作用?

package

main

// 仍然定義一個結構體

type

actorix

struct

{

id

int

name

string

number

float64

}

func

main

()

{

// 初始化一個actorix

var

a

=

actorix

{

id

3

name

“vs”

number

8。99

}

// 注意這裡很關鍵我們傳入的是&a

var

atype

=

reflect

ValueOf

&

a

fmt

Println

“kind of atype is: ”

atype

Kind

())

atype

=

atype

Elem

()

fmt

Println

“kind of atype。Elem() is: ”

atype

Kind

())

fmt

Println

“value in atype is: ”

atype

}

// 列印結果:

// kind of atype is: ptr

// kind of atype。Elem() is: struct

// value in atype is: {3 vs 8。99}

如果我們把

var atype = reflect。ValueOf(&a)

改為

var atype = reflect。ValueOf(a)

func

main

()

{

var

a

=

actorix

{

id

3

name

“vs”

number

8。99

}

var

atype

=

reflect

ValueOf

a

fmt

Println

“kind of atype is: ”

atype

Kind

())

// 此時要注意這裡的atype和a不是一回事,指向的地址不一樣,atype是a在堆中的複製

// 這裡編譯器會阻止一切修改:

// 如果新增程式碼: atype。FieldByName(“id”)。SetInt(9) ,就會報錯

fmt

Println

“value in atype is: ”

atype

}

// 列印結果:

// kind of atype is: struct

// value in atype is: {3 vs 8。99}

2 應用反射功能寫一個簡單DI依賴注入容器

首先我們來簡單講一下什麼是依賴注入。舉個不恰當的例子:

某位老闆想要做假賬騙錢,於是對財務總監說:你把賬調整一下,財務總監心領神會於是對手下的老財說:你把賬調整一下,老闆等著要!

很顯然上述過程中,老闆最終需要的是老財調整好的賬本,但他不會直接指揮老財,而是下達命令給財務總監。老闆本人是不會做賬,他等著財務總監給他反饋;而財務總監也是不做賬的,他等著老財給他反饋。

老闆想要的結果

依賴於

財務總監的執行情況,財務總監想要的結果

依賴於

老財的執行結果。

這就是依賴注入想要解決的問題,在現實大型程式中,任務鏈可能非常長,如果企業層層上報的體制一樣,一個類的實現可能依賴於很多呼叫鏈上的其他類的實現,但我們只想做老闆,只關心本層的實現,並不想關心下一層會怎麼做,就像我們例子中老闆並不在意這件調賬這項工作到底是財務總監做還是財務總監手下的老財做,所以我們需要一個機制,幫助我們一旦發出呼叫某個類的指令,就會事先自動呼叫這個類的實現,這同樣可以理解為控制反轉。

我們先來做一個最簡單的基礎原型,為了簡便起見我們只設定兩層依賴(從財務總監發出order到老財),然後把依賴注入全部放到一個

diInvoke()

函式當中:

// Accountant 我們先設計一個老財結構體

type

Accountant

struct

{

name

string

}

// ctorAccountant 初始化Accountant結構體的函式,其返回值為Accountant

func

ctorAccountant

()

Accountant

{

return

Accountant

{

name

“十年老會計”

}

}

// order 命令函式,傳入引數是Accountant,我們需要一個指揮老財幫我們做賬

// order的呼叫依賴於Accontant結構體的構建

func

order

a

Accountant

{

fmt

Println

“需要去做假賬的老財是: ”

a

name

}

// 這是我們的依賴注入函式,我們預期的效果是,他傳入一個函式型別的引數(也就是order函式)

// 然後會自動解析order的形參(也就是Accountant結構體)

// 根據這個形參型別自動去一張dimap中尋找返回值為該形參型別的函式(也就是ctorAccountant函式)

// 然後呼叫該函式,並將其結果儲存在一張形引數組中,此時order函式所需要的形參已經構建完畢

// 最後呼叫order函式本身

// 完畢

func

diInvoke

function

interface

{})

error

{

// 新建一張dimap我們要將ctorAccountant函式註冊進去

// dimap的key是ctorAccountant返回值的reflect。Type

// dimap的value是ctorAccountant本身的reflect。Value(也可以理解為reflect。Value結構體模式的函式指標)

var

dimap

=

make

map

reflect

Type

reflect

Value

var

funcs

=

ctorAccountant

// 透過ValueOf函式我們將ctorAccountant轉換為Value結構體

var

t

=

reflect

ValueOf

funcs

// vt儲存ctorAccountant的Type型別

var

vt

=

t

Type

()

// 我們這裡使用vt。NumOut()獲取所有ctorAccountant函式的返回值數量

// 並用vt。Out()函式將所有返回值型別儲存再一個results陣列中(Type。Out()返回值型別為reflect。Type)

var

results

=

make

([]

reflect

Type

vt

NumOut

())

for

i

:=

0

i

<

vt

NumOut

();

i

++

{

// Out函式原型: Out func(i int) Type 返回第i個型別變數的返回值

results

i

=

vt

Out

i

}

// 同理這裡處理的是形參,但與返回值的處理不同,形參我們不能儲存為reflect。Type的陣列

// 需要儲存為reflect。Value的陣列,這是因為後期的函式呼叫Value。Call()能夠接收的引數為[]Value

var

params

=

make

([]

reflect

Value

vt

NumIn

())

for

i

:=

0

i

<

vt

NumIn

();

i

++

{

// In函式原型: In func(i int) Type,返回型別變數的第i個形參,返回的是一個Type型別,這裡轉換為ValueOf

params

i

=

reflect

ValueOf

vt

In

i

))

}

// 我們這裡先分別列印一下形引數組params[]和返回值陣列results[]

fmt

Println

“形引數組為: ”

params

fmt

Println

“返回值陣列為: ”

results

// 建立dimap {key:返回值型別,value:t的reflect。Value形式}

// 由於我們知道results中只有一個值,我們這裡為了方便起見直接制定了要插入的result

dimap

results

0

]]

=

t

// 列印下dimap

fmt

Println

“依賴注入表: ”

dimap

return

nil

}

go語言反射探秘

可見我們當前的形引數組為空,這個很正常,因為我們

orderAccountant

本身就是沒有形參。返回值陣列只有一個main。Accountant,而dimap為

map[main。Accountant:

返回值與該函式形成了對應關係。

// 剩餘部分

func

diInvoke

function

interface

{})

error

{

// 。。。snip。。。

// 接下來終於輪到我們對傳入的形參進行處理了,別忘了,本函式的形參function就是order函式

// 同樣我們需要用reflect。ValueOf獲取function本身的reflect。Value結構體

var

vf

=

reflect

ValueOf

function

// 先判斷其是不是函式型別,不是的化報錯

if

vf

Kind

()

!=

reflect

Func

{

return

fmt

Errorf

“constructor must be a func”

}

// 獲取order函式的reflect。Type介面形式

var

vft

=

vf

Type

()

// 獲取order函式本身的形參列表,並將其放到陣列vfparams中,注意陣列型別同樣是reflect。Value

var

vfparams

=

make

([]

reflect

Value

vft

NumIn

())

for

i

:=

0

i

<

vft

NumIn

();

i

++

{

// 這裡是最關鍵的,dimap中的key是返回值,也就是當前order的形參(a Accountant)

// 所以我們可以用dimap[vft。In(i)]獲取對應的函式

// 然後呼叫Call(params)直接執行引數生成結果,Call將返回結果包裝為一個Value[]陣列(可能存在多個返回值)

// 這樣一來,我們就已經將function的形參都建立好了

vfparams

i

=

dimap

vft

In

i

)]。

Call

params

)[

i

}

// 我們這裡先分別列印一下形引數組vfparams[]

fmt

Println

“形引數組為: ”

vfparams

// 直接執行order函式

vf

Call

vfparams

return

nil

}

我們列印vfparams的結果為:

go語言反射探秘

// 函式呼叫

func

main

()

{

diInvoke

order

}

// 顯示結果

// 需要去做假賬的老財是: 十年老會計

總體過程如下:

1 我們有一個函式:

func ctorAccountant() Accountant

2 然後將其註冊到一張表dimap裡面:

鍵key

值value

Accountant

ctorAccountant函式

3 指令函數出現:

func order(a Accountant)

4 指令函式需要傳入的形參型別是

Accountant

,於是查表找到鍵為

Accountant

的值

5 用Call方法呼叫

Accountant

鍵對應的函式

ctorAccountant

6 將返回的結果ctorAccountant返回的結果儲存在一個

vfparams

陣列中,依賴完成

7 呼叫order函式,將

vfparms

的形參傳入其中,order函式執行完畢

當然這是個最簡單模型,甚至只有兩層呼叫,接下來我們要看一個更復雜的例子:

假設我們現在存在三層組織結構:

Director(總監) -> Manager(經理) -> Accountant(老財)

Director和Manager下都有成員型別workgrounp為他們的下級,老財作為最底層只有string型別的name欄位。他們分別有各自的構建函式,他們的構建關係分別同他們的層級依賴在一起:

Director(總監)的構建需要依賴Manager(經理)的構建,Manager(經理)的構建需要依賴Accountant(老財)的構建,Accountant(老財)構建自己本身無依賴

type

Director

struct

{

workgrounp

*

Manager

}

type

Manager

struct

{

workgrounp

*

Accountant

}

type

Accountant

struct

{

name

string

}

func

ctorDirector

manager

*

Manager

*

Director

{

return

&

Director

{

workgrounp

manager

}

}

func

ctorManager

accountant

*

Accountant

*

Manager

{

return

&

Manager

{

workgrounp

accountant

}

}

func

ctorAccountant

()

*

Accountant

{

return

&

Accountant

{

name

“十年老會計”

}

}

同時我們要再定義一個order結構體,用於存放所有要去執行的指令:

// 宣告一個函式指標型別

type

orderFunc

func

()

// 一個用於存放財務報表的結構體

type

FinanceStatement

struct

{

assets

float32

liabilities

float32

equities

float32

}

// 三個指令函式

// 做假帳直接,反饋財務報表的資料

func

modifyFinanceStatement

()

{

var

f

=

FinanceStatement

{

assets

233。00

liabilities

211。00

equities

22。00

}

fmt

Println

“賬務調整完畢, 返回報告”

fmt

Printf

“調整好得報表為: %+v\n”

f

}

// 老財背鍋

func

blameAccountant

()

{

fmt

Println

“都是老財的錯!”

}

// 解僱老財

func

fireAccountant

()

{

fmt

Println

“老財被開除”

}

// order結構體用於存放一張map,我們將指令說明和指令函式存放其中

type

order

struct

{

orderMap

map

string

orderFunc

}

// oder的構建函式,我們將三個指令函式在order初始化的時候就註冊進去

func

ctorOrder

()

*

order

{

var

ordermap

=

make

map

string

orderFunc

ordermap

“做假帳”

=

modifyFinanceStatement

ordermap

“老財背鍋”

=

blameAccountant

ordermap

“解僱老財”

=

fireAccountant

return

&

order

{

orderMap

ordermap

}

}

// 未來有新的指令函式我們可以呼叫order。addOrder將其新增到order結構體的orderMap當中

func

o

*

order

addOrder

desc

string

function

orderFunc

{

o

orderMap

desc

=

function

}

Director(總監)、 Manager(經理)、 Accountant(老財)分別有成員函式execute_report_order,用於執行和反饋上級下達的任務,到了Accountant(老財)這一層,會讀取oderMap中所有需要執行的指令,並執行

func

director

*

Director

execute_report_order

om

map

string

orderFunc

{

director

workgrounp

execute_report_order

om

}

func

manager

*

Manager

execute_report_order

om

map

string

orderFunc

{

manager

workgrounp

execute_report_order

om

}

func

accountant

*

Accountant

execute_report_order

om

map

string

orderFunc

{

fmt

Println

“任務收到,開始執行。。。。執行人”

accountant

name

for

key

orderfunc

:=

range

om

{

fmt

Printf

“指令:%v ”

key

orderfunc

()

}

fmt

Println

“所有指令執行完畢”

}

最後我們設計一個命令函式

order()

,作用如下:

老闆對總監(Director)發出了一堆任務,總監執行

execute_report_order

函式,並反饋任務成果。

func

dispatch_order

d

*

Director

o

*

order

{

d

execute_report_order

o

orderMap

}

如果不使用依賴注入,我們通常要這麼呼叫任務鏈:

我們必須從下到上依次構建,這個順序不能錯

老財->經理->總監

,然後初始化

order

指令結構體

func

RunDIfactorymeTest

()

{

var

a

=

ctorAccountant

()

var

m

=

ctorManager

a

var

d

=

ctorDirector

m

var

o

=

ctorOrder

()

dispatch_order

d

o

}

而我們現在期望:建立一個container容器,將4個ctor構建函式都註冊到container容器中形成一張dimap,然後把order函式直接扔進container。Invoke,依賴構建自動完成,並執行order函式獲得結果。

容器的設計如下:

go語言反射探秘

容器程式碼如下,參考來源:

程式碼參考

package

di

import

“fmt”

“reflect”

// DIContainer DI容器,主要是一張dimap和一個快取map

type

DIContainer

struct

{

dimap

map

reflect

Type

funcHandle

resultBuffer

map

reflect

Type

reflect

Value

}

// funcHandle 是函式控制代碼,主要是儲存函式指標和其形參列表

type

funcHandle

struct

{

funcPtr

reflect

Value

params

[]

reflect

Type

}

func

fn

*

funcHandle

runFunc

params

[]

reflect

Value

[]

reflect

Value

{

return

fn

funcPtr

Call

params

}

// DIContainerInitial 建立容器

func

DIContainerInitial

()

*

DIContainer

{

return

&

DIContainer

{

dimap

map

reflect

Type

funcHandle

{},

resultBuffer

map

reflect

Type

reflect

Value

{},

}

}

// Register 註冊函式,我們需要將外部的構建函式全部註冊到容器當中

// 1 先獲得reflect。Value形式的ctorfunction(reflect。ValueOf函式)

// 2 判斷其是否為函式型別

// 3 然後獲得其Type型別

// 4 根據其Type型別的介面函式獲得它的形參和返回值

// 5 將形參和函式本身的reflect。Value形式存放到funcHandle結構體中

// 6 將返回值型別作為key,將funcHandle作為value,存到dimap表中

func

c

*

DIContainer

Register

ctorfunction

interface

{})

error

{

var

funcValue

=

reflect

ValueOf

ctorfunction

if

funcValue

Kind

()

!=

reflect

Func

{

return

fmt

Errorf

“註冊進來的形參必須為函式型別”

}

var

funcType

=

funcValue

Type

()

// 獲取引數NumIn是返回函式的引數列表的數量

// 我們In(i)函式返回所有的引數列表,將其將其儲存在一個reflect。Type型別的陣列當中

// 實際上In函式呼叫了type。go中的in()函式,函式原型如下,其實返回的是一個所有函式引數的陣列

// 只不過in不提供對外介面,只有透過In(i)形式來獲取in[i]

// func (t *funcType) in() []*rtype {

// uadd := unsafe。Sizeof(*t)

// if t。tflag&tflagUncommon != 0 {

// uadd += unsafe。Sizeof(uncommonType{})

// }

// if t。inCount == 0 {

// return nil

// }

// return (*[1 << 20]*rtype)(add(unsafe。Pointer(t), uadd, “t。inCount > 0”))[:t。inCount:t。inCount]

// }

// 獲取所有形參列表

var

params

=

make

([]

reflect

Type

funcType

NumIn

())

for

i

:=

0

i

<

funcType

NumIn

();

i

++

{

params

i

=

funcType

In

i

}

// 獲取所有返回值列表

var

returns

=

make

([]

reflect

Type

funcType

NumOut

())

for

i

:=

0

i

<

funcType

NumOut

();

i

++

{

returns

i

=

funcType

Out

i

}

var

funchandle

=

funcHandle

{

funcPtr

funcValue

params

params

}

// 將返回值作為key,funchandle作為value註冊到dimap

for

_

r

:=

range

returns

{

// 判斷當前返回值對應的funchandle是否已經

if

_

ok

:=

c

dimap

r

];

ok

{

continue

}

c

dimap

r

=

funchandle

}

return

nil

}

// 列印dimap

func

c

*

DIContainer

Printdimap

()

{

fmt

Println

“the container。providers is: ”

c

dimap

}

// 列印resultbuffer

func

c

*

DIContainer

PrintResultsBuffer

()

{

fmt

Println

“the container。results is: ”

c

resultBuffer

}

// Invoke 啟動函式,我們將要執行的指令函式放入形參fn中

// 1 先獲得reflect。Value形式的fn(reflect。ValueOf函式)

// 2 判斷其是否為函式型別

// 3 然後獲得其Type型別

// 4 根據其Type型別的介面函式獲得它的形參

// 6 將fn本身的形參存入一個形引數組params

// 7 這些形參可能存在需要我們取自動化依賴構建的函式

// 8 呼叫依賴構建函式,自動對每個形參進行構建,完成後傳入形引數組

// 注意形引數組的型別是reflect。Value,這是為了對應Value。Call()函式:

// Value。Call()函式原型,可見其主要功能是先驗證v是否為函式型別,再呼叫call指令執行函式

// func (v Value) Call(in []Value) []Value {

// v。mustBe(Func)

// v。mustBeExported()

// return v。call(“Call”, in)

// }

func

c

*

DIContainer

Invoke

fn

interface

{})

error

{

var

funcValue

=

reflect

ValueOf

fn

if

funcValue

Kind

()

!=

reflect

Func

{

return

fmt

Errorf

“註冊進來的形參必須為函式型別”

}

var

funcType

=

funcValue

Type

()

// 獲取fn本身的形參存入臨時的形引數組

var

err

error

var

params

=

make

([]

reflect

Value

funcType

NumIn

())

for

i

:=

0

i

<

funcType

NumIn

();

i

++

{

params

i

],

err

=

c

diConstructor

funcType

In

i

))

if

err

!=

nil

{

return

err

}

}

funcValue

Call

params

return

nil

}

// diConstructor 依賴構建器

// 1 傳入param的是外部指令函式的形參,先檢視resultBuffer中是否已經有對應的結果,如果有直接返回

// 2 去dimap檢視是否存在該形參對應的param

// 3 不存在就返回一個ValueOf(param)將其轉換為Value模式

// 4 如果存在則獲取相應的函式控制代碼funchandle = dimap[parm],不存在返回error

// 5 然後解包funchandle,讀取對應的的funchandle。params

// 6 對每個funchandle。params進行遞迴地呼叫diConstructor(),直道遞迴到不存在形參無依賴的底層函式

// 7 執行函式,層層返回結果

// 8 將函式執行的結果儲存在一個results陣列中陣列型別為reflect。Value

// 9 將其寫入容器的resultBuffer中

func

c

*

DIContainer

diConstructor

param

reflect

Type

val

reflect

Value

err

error

{

// 如果容器的resultBuffer已經存在形參param和result的對應,那麼直接返回

if

result

ok

:=

c

resultBuffer

param

];

ok

{

return

result

nil

}

// dimap中找不到對應的函式控制代碼

var

funchandle

ok

=

c

dimap

param

if

ok

{

return

reflect

Value

{},

fmt

Errorf

“當前形參%s, 沒有對應的函式控制代碼”

param

}

// 建立一個reflect。Value的陣列,用來存放所有funchandle。params

var

params

=

make

([]

reflect

Value

len

funchandle

params

))

for

i

p

:=

range

funchandle

params

{

// 這裡需要對每個funchandle裡的形參進行遞迴構建

// 逐步講解如下:

// 1 是order(d *Director),然後我們獲取order的形參Director並執行diConstructor(Director)

// 2 對d進行查表後得到其對應的map[Director] = ctorDirector

// 3 對ctorDirector的函式控制代碼進行解包,重新獲得其函式形參ctorDirector(manager *Manager)

// 4 將ctorDirector的形參重新作為形參傳遞迴遞給diConstructor(Manager)

// 5 查表後獲得map[Manager] = ctorManager

// 6 對ctorManage的函式控制代碼進行解包,重新獲得其函式形參ctorManager(accountant *Accountant)

// 7 將ctorManager的形參重新作為形參傳遞給diConstructor(Accountant)

// 8 查表後獲得map[Accountant] = ctorAccountant

// 9 對ctorAccountant的函式控制代碼進行解包

// 10 重新獲得其函式形參ctorAccountant()為reflect。Type型別空值

// 11 繼續呼叫diConstructor([])

// 12 空的形參會跳過for i, p := range funchandle。params{}這個迴圈體,直接執行依賴函式

params

i

],

err

=

c

diConstructor

p

}

// 執行依賴函式,如果已經來到了依賴底層,那麼這裡的params是一個reflect。Value型別的空陣列

// results 返回的是一個Value型別的結果陣列[]reflect。Value

// 假設我們這裡如果呼叫的是最深層的Accountant結構體

// results = [Accountant]

var

results

=

funchandle

runFunc

params

// 遍歷返回結果並做驗證

for

_

result

:=

range

results

{

// 如果results中返回的結果有error型別(diConstructor其中一個返回值是error),則報錯

if

validateError

result

Type

())

&&

result

IsNil

()

{

return

reflect

Value

{},

fmt

Errorf

“%+v 函式控制代碼呼叫錯誤: %+v”

funchandle

result

}

// 不存在error且不為nil,則將其儲存到容器的resultBuffer中

if

validateError

result

Type

())

&&

result

IsNil

()

{

// 這裡我們放個除錯列印資訊,可以方便我們逐步檢視resultbuffer的變化

fmt

Println

“c。resultBuffer[”

result

Type

(),

“] = ”

result

c

resultBuffer

result

Type

()]

=

result

}

}

// 這裡我們放個除錯列印資訊,可以方便我們逐步檢視每次返回的內容

fmt

Println

“c。resultBuffer[”

param

“] = ”

c

resultBuffer

param

])

return

c

resultBuffer

param

],

nil

}

func

validateError

t

reflect

Type

bool

{

if

t

Kind

()

!=

reflect

Interface

{

return

false

}

// Implements reports whether the type implements the interface type u。

// 原型:Implements(u Type) bool

// reflect。TypeOf(reflect。TypeOf((*error)(nil)) => *error

// reflect。TypeOf(reflect。TypeOf((*error)(nil))。Elem() => error

// 這裡也驗證了Elem()是解引用的操作

// 實際上這條可以寫為: t。Implements(error)

return

t

Implements

reflect

TypeOf

reflect

TypeOf

((

*

error

)(

nil

))。

Elem

()))

}

呼叫部分程式碼如下:

package

di

import

“fmt”

type

Director

struct

{

workgrounp

*

Manager

}

type

Manager

struct

{

workgrounp

*

Accountant

}

type

Accountant

struct

{

name

string

}

type

FinanceStatement

struct

{

assets

float32

liabilities

float32

equities

float32

}

type

orderFunc

func

()

func

modifyFinanceStatement

()

{

var

f

=

FinanceStatement

{

assets

233。00

liabilities

211。00

equities

22。00

}

fmt

Println

“賬務調整完畢, 返回報告”

fmt

Printf

“調整好得報表為: %+v\n”

f

}

func

blameAccountant

()

{

fmt

Println

“都是老財的錯!”

}

func

fireAccountant

()

{

fmt

Println

“老財被開除”

}

type

order

struct

{

orderMap

map

string

orderFunc

}

func

ctorOrder

()

*

order

{

var

ordermap

=

make

map

string

orderFunc

ordermap

“做假帳”

=

modifyFinanceStatement

ordermap

“老財背鍋”

=

blameAccountant

ordermap

“解僱老財”

=

fireAccountant

return

&

order

{

orderMap

ordermap

}

}

func

o

*

order

addOrder

desc

string

function

orderFunc

{

o

orderMap

desc

=

function

}

func

ctorDirector

manager

*

Manager

*

Director

{

return

&

Director

{

workgrounp

manager

}

}

func

ctorManager

accountant

*

Accountant

*

Manager

{

return

&

Manager

{

workgrounp

accountant

}

}

func

ctorAccountant

()

*

Accountant

{

return

&

Accountant

{

name

“十年老會計”

}

}

func

director

*

Director

execute_report_order

om

map

string

orderFunc

{

director

workgrounp

execute_report_order

om

}

func

manager

*

Manager

execute_report_order

om

map

string

orderFunc

{

manager

workgrounp

execute_report_order

om

}

func

accountant

*

Accountant

execute_report_order

om

map

string

orderFunc

{

fmt

Println

“任務收到,開始執行。。。。執行人”

accountant

name

for

key

orderfunc

:=

range

om

{

fmt

Printf

“指令:%v ”

key

orderfunc

()

}

fmt

Println

“所有指令執行完畢”

}

func

dispatch_order

d

*

Director

o

*

order

{

d

execute_report_order

o

orderMap

}

func

RunDIfactorymeTest

()

{

// 初始化容器

var

dicontainer

=

DIContainerInitial

()

// 將構建函式全部註冊進去,可以不必理會順序

if

err

:=

dicontainer

Register

ctorAccountant

);

err

!=

nil

{

panic

err

}

if

err

:=

dicontainer

Register

ctorManager

);

err

!=

nil

{

panic

err

}

if

err

:=

dicontainer

Register

ctorDirector

);

err

!=

nil

{

panic

err

}

if

err

:=

dicontainer

Register

ctorOrder

);

err

!=

nil

{

panic

err

}

// 列印一下函式註冊後的dimap

fmt

Println

“dicontainer。dimap: ”

dicontainer

Printdimap

()

// 列印一下函式Invoke執行前的resultbuffer,應該為空

fmt

Println

“dicontainer。resultbuffer before Invoke(): ”

dicontainer

PrintResultsBuffer

()

// 啟動Invoke指令,我們將命令下達

var

err

=

dicontainer

Invoke

dispatch_order

// 列印一下函式Invoke執行後的resultbuffer,應該所有依賴構建後的資料都儲存其中

fmt

Println

()

fmt

Println

“container。resultbuffer after Invoke(): ”

dicontainer

PrintResultsBuffer

()

if

err

!=

nil

{

panic

err

}

}

// 列印顯示:

// 任務收到,開始執行。。。。執行人 十年老會計

// 指令:解僱老財 老財被開除

// 指令:做假帳 賬務調整完畢, 返回報告

// 調整好得報表為: {assets:233 liabilities:211 equities:22}

// 指令:老財背鍋 都是老財的錯!

// 所有指令執行完畢

執行結果:

go語言反射探秘

標簽: reflect  函式  Order  value  type