ir al decorador de función de lenguaje, tipo de interfaz asignación de reflexión variable

decoración funcional

A menudo es necesario utilizar la decoración de funciones para hacer componentes básicos. Por ejemplo, necesito imprimir el inicio y el final de todos los métodos decorados.

Decoración para firmas de funciones conocidas

Los decoradores de funciones que usamos a menudo generalmente conocen la firma del método que se está decorando y luego devuelven un método con la misma firma. El ejemplo más simple, el middleware de kite , en realidad decora el método del controlador.

type EndPoint func(ctx context.Context, req interface{}) (resp interface{}, err error)
func Middleware(next endpoint.EndPoint) endpoint.EndPoint {
   return func(ctx context.Context, req interface{}) (resp interface{}, err error) {
      // before do something...
      next(ctx, req)
      // after do something...
   }
}

Entonces, es esencialmente la forma de llamada de fa (fb(fc( fd (req)))) .

La base de esta decoración de funciones es que cada método decorado conoce la firma de la función decorada. Cuando se cambia la firma de la función, el método decorado no está disponible.

¿Cómo resolver la decoración de funciones genéricas?

El decorador es esencialmente para generar un func, y el paquete de reflexión tiene un método general para generar func, golang.org/pkg/reflect…

func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
MakeFunc returns a new function of the given Type that wraps the function fn. When called, that new function does the following:

- converts its arguments to a slice of Values.
- runs results := fn(args).
- returns the results as a slice of Values, one per formal result.

Los parámetros de entrada de makefunc son type y func, por lo que debe resolver el tipo del método de decoración, puede usar el método TypeOf en el paquete de reflexión.

func TypeOf(i interface{}) Type

Para decorar una función genérica, los parámetros de entrada de todos los decoradores deben ser interfaces, luego el diseño preliminar del decorador es

func Decorate(f interface{}) interface{} {
   fn := reflect.ValueOf(f)
   v := reflect.MakeFunc(fn.Type(), logicFunc)
   return v.Interface()
}

El siguiente paso es resolver el problema de logicFunc. Según la descripción de MakeFunc, v es una función con un tipo superior, y el resultado de v(args) es el resultado de ejecución de logicFunc(args), por lo que la lógica de logicFunc es lo que debe hacer el decorador, la firma del método de logicFunc se ha corregido

func(args []Value) (results []Value)

Entonces, considere directamente la implementación de logicFunc, hay un paquete de reflexión

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

Se puede realizar la llamada de cualquier función después de la reflexión, por lo que la función original también se puede usar directamente en logicFunc, de forma análoga a next(ctx, req) en el middleware.

func(in []reflect.Value) []reflect.Value { 
   fn := reflect.ValueOf(f)
   // ...
   ret := fn.Call(in)
   // ...
   return ret
}

Así que el decorador de funciones genéricas final es

func Decorate(f interface{}) interface{} {
   fn := reflect.ValueOf(f)
   logicFunc := func(in []reflect.Value) []reflect.Value { 
   	fn := reflect.ValueOf(f)
   	// before do something...
   	ret := fn.Call(in)
   	// after do something...
   	return ret
	}
   v := reflect.MakeFunc(fn.Type(), logicFunc)
   return v.Interface()
}

Este decorador sigue siendo incómodo de usar.

func foo(a, b, c int) (int, error) {
   return a + b + c, nil
}

func main() {
   decorateFoo := Decorate(foo2)
   if fn, ok := decorateFoo.(func(a, b, c int) (int, error)); ok {
      ret, err := fn(1, 2, 3)
      fmt.Println(ret, err)
   }
}

La firma del método decorado debe confirmarse antes de cada uso.

能不能不进行断言呢,因为被修饰的方法本身就是入参,但是为了通用性,入参必须定义为interface,那么考虑出参也在使用前定义好签名。也就是装饰器把返回值当入参由使用方来传入。这也是左耳朵耗子推荐的用法

func Decorate(decoPtr, f interface{}) error {
   fn := reflect.ValueOf(f)
   decoratedFunc := reflect.ValueOf(decoPtr).Elem()
   logicFunc := func(in []reflect.Value) []reflect.Value { 
   	// before do something...
   	ret := fn.Call(in)
   	// after do something...
   	return ret
	}
   v := reflect.MakeFunc(fn.Type(), logicFunc)
   decoratedFunc.Set(v)
   return nil
}

使用示例

func foo1(a, b, c int) (int, error) {
   return a + b + c, nil
}

func main() {
   decorateFoo := foo1
   Decorate(&decorateFoo, foo1)
   ret, err := decorateFoo(1, 2, 3)
   fmt.Println(ret, err)
}

反射更改接口类型的值

有这个想法是因为在通用函数的装饰里,假设所有方法的返回值最后一位都是error,但每个方法签名不一样,怎么能做到在通用装饰里,把所有的返回error置为nil

先看普通变量赋值场景

reflect包里有set**接口,其中通用set接口说明为

func (v Value) Set(x Value)

Set assigns x to the value v. It panics if CanSet returns false. As in Go, x's value must be assignable to v's type.

那么对普通变量进行赋值

var a int64
v := reflect.ValueOf(a)
v.Set(reflect.ValueOf(int64(3)))
println(a)

这段代码是会panic的,可以看set接口的的说明,查看canset为false

println(v.CanSet())

因为go都是值复制的,所以v内的a只是个副本,不能set就好理解了。稍微变通一下就可以了,利用地址传递。 直接来个普通变量正常赋值的情况

var a int64
v := reflect.ValueOf(&a).Elem()
println(v.CanSet())
v.Set(reflect.ValueOf(int64(3)))
println(a)

对接口类型反射赋值

我们先定义一个接口和实现类

type itf interface {
   String() string
}

type impl struct {
}

func (*impl) String() string {
   return "itf impl"
}

参考普通变量赋值,可以写接口的赋值方法

func main() {
   var a itf
   v := reflect.ValueOf(&a).Elem()
   println(v.CanSet())

   actual := &impl{}
   v.Set(reflect.ValueOf(actual))
   println(a.String())
}

对接口类型反射置nil

有的场景下,是接口类型有值,但是要把这个值置空,例如感知到下游的err后要返回nil,但是每个方法的签名不一样,只知道返回值最后一位是err

var a itf
a = &impl{}
v := reflect.ValueOf(&a).Elem()
println(v.CanSet())

v.Set(reflect.ValueOf(nil))
println(a)

上述代码会直接保存,因为对nil进行反射取value得到的是空结果,set不进去。 分析我们需要set的其实是个接口itf的0值,reflect包里是有0值构造器的

func Zero(typ Type) Value

Zero方法的入参typ 应该是我们需要操作的接口类型itf, 很自然会想到

var b itf
zero := reflect.Zero(reflect.TypeOf(b))
v.Set(zero)

但是这里的b实际上也是nil,所以reflect.Typeof(nil)并不会返回itf的type。所以需要转一道, 取b的地址,然后利用elem取值

var b itf
zero := reflect.Zero(reflect.TypeOf(&b).Elem())
v.Set(zero)

所以把接口类型变量置nil的完整写法应该是

func main() {
   var a itf
   a = &impl{}
   v := reflect.ValueOf(&a).Elem()
   println(v.CanSet())

   var b itf
   zero := reflect.Zero(reflect.TypeOf(&b).Elem())
   v.Set(zero)
   println(a)
   println(a == nil)
}

分析发现 var b itf实际上是多余的,只是为了一个类型,可以省略掉

zero := reflect.Zero(reflect.TypeOf((*itf)(nil)).Elem())

综上完成了对接口类型置nil的操作。

Supongo que te gusta

Origin juejin.im/post/7115343063119036453
Recomendado
Clasificación