go 函数参数nil_go语言反射探秘

以下分为两个部分讲讲go中的反射:

第一部分是核心要点TypeOfValueOf

第二部分是应用反射功能写一个简单DI依赖注入容器

1 TypeOfValueOf

c0a36d0648d0b69772a92c06246dc28e.png

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
}

091df773a2a01c4ac04b4d0b3095f9cc.png

可见我们当前的形参数组为空,这个很正常,因为我们orderAccountant本身就是没有形参。返回值数组只有一个main.Accountant,而dimap为map[main.Accountant:<func() main.Accountant Value>] 返回值与该函数形成了对应关系。

// 剩余部分
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的结果为:

32bbeff83a11ece242fe583441fb8bf3.png
// 函数调用
func main() {
    diInvoke(order)
}
// 显示结果
// 需要去做假账的老财是:  十年老会计

总体过程如下:

1 我们有一个函数:func ctorAccountant() Accountant

2 然后将其注册到一张表dimap里面:

键key值value
AccountantctorAccountant函数

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("调整好得报表为: %+vn", 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函数获得结果。

容器的设计如下:

69d6529c2e7b1a5262d2ef10045b4970.png

容器代码如下,参考来源:代码参考

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("调整好得报表为: %+vn", 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}
// 指令:老财背锅   都是老财的错!
// 所有指令执行完毕

执行结果:

fa5dbeda65ed23e242e3a6d00a1055e6.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值