是否可以在Go中为任意函数创建一个包装器,它将采用相同的参数并返回相同的值?
我不是在谈论看起来完全相同的包装器,它可能看起来不同,但它应该解决问题。
例如问题可能是创建一个任意函数的包装器,它首先在缓存中查找函数调用的结果,并且只有在缓存未命中的情况下执行包装函数。
答案 0 :(得分:2)
这是使用reflect.MakeFunc的解决方案。此特定解决方案假定您的转换函数知道如何处理每种不同类型的函数。观看此操作:http://play.golang.org/p/7ZM4Hlcqjr
package main
import (
"fmt"
"reflect"
)
type genericFunction func(args []reflect.Value) (results []reflect.Value)
// A transformation takes a function f,
// and returns a genericFunction which should do whatever
// (ie, cache, call f directly, etc)
type transformation func(f interface{}) genericFunction
// Given a transformation, makeTransformation returns
// a function which you can apply directly to your target
// function, and it will return the transformed function
// (although in interface form, so you'll have to make
// a type assertion).
func makeTransformation(t transformation) func(interface{}) interface{} {
return func(f interface{}) interface{} {
// g is the genericFunction that transformation
// produced. It will work fine, except that it
// takes reflect.Value arguments and returns
// reflect.Value return values, which is cumbersome.
// Thus, we do some reflection magic to turn it
// into a fully-fledged function with the proper
// type signature.
g := t(f)
// typ is the type of f, and so it will also
// be the type that of the function that we
// create from the transformation (that is,
// it's essentially also the type of g, except
// that g technically takes reflect.Value
// arguments, so we need to do the magic described
// in the comment above).
typ := reflect.TypeOf(f)
// v now represents the actual function we want,
// except that it's stored in a reflect.Value,
// so we need to get it out as an interface value.
v := reflect.MakeFunc(typ, g)
return v.Interface()
}
}
func main() {
mult := func(i int) int { return i * 2 }
timesTwo := func(f interface{}) genericFunction {
return func(args []reflect.Value) (results []reflect.Value) {
// We know we'll be getting an int as the only argument,
// so this type assertion will always succeed.
arg := args[0].Interface().(int)
ff := f.(func(int) int)
result := ff(arg * 2)
return []reflect.Value{reflect.ValueOf(result)}
}
}
trans := makeTransformation(timesTwo)
// Since mult multiplies its argument by 2,
// and timesTwo transforms functions to multiply
// their arguments by 2, f will multiply its
// arguments by 4.
f := trans(mult).(func(int) int)
fmt.Println(f(1))
}
答案 1 :(得分:2)
答案基于@ joshlf13的想法和答案,但对我来说似乎更简单。 http://play.golang.org/p/v3zdMGfKy9
package main
import (
"fmt"
"reflect"
)
type (
// Type of function being wrapped
sumFuncT func(int, int) (int)
// Type of the wrapper function
wrappedSumFuncT func(sumFuncT, int, int) (int)
)
// Wrapper of any function
// First element of array is the function being wrapped
// Other elements are arguments to the function
func genericWrapper(in []reflect.Value) []reflect.Value {
// this is the place to do something useful in the wrapper
return in[0].Call(in[1:])
}
// Creates wrapper function and sets it to the passed pointer to function
func createWrapperFunction(function interface {}) {
fn := reflect.ValueOf(function).Elem()
v := reflect.MakeFunc(reflect.TypeOf(function).Elem(), genericWrapper)
fn.Set(v)
}
func main() {
var wrappedSumFunc wrappedSumFuncT
createWrapperFunction(&wrappedSumFunc)
// The function being wrapped itself
sumFunc := func (a int, b int) int {
return a + b
}
result := wrappedSumFunc(sumFunc, 1, 3)
fmt.Printf("Result is %v", result)
}
答案 2 :(得分:1)
我提出的最好的方法是使用函数def并返回一个接口,之后需要进行类型断言:
func Wrapper(metaParams string, f func() (interface{}, string, error)) (interface{}, error) {
// your wrapper code
res, metaResults, err := f()
// your wrapper code
return res, err
}
然后使用它还需要一些功能就像包装器一样:
resInterface, err := Wrapper("data for wrapper", func() (interface{}, string, error) {
res, err := YourActualFuntion(whatever, params, needed)
metaResults := "more data for wrapper"
return res, metaResults, err
}) // note f() is not called here! Pass the func, not its results
if err != nil {
// handle it
}
res, ok := resInterface.(actualType)
if !ok {
// handle it
}
好处是有些通用,可以处理1种返回类型+错误,并且不需要反射。
缺点是需要做大量的工作,因为它不是一个简单的包装器或装饰器。
答案 3 :(得分:0)
喜欢这个吗?
var cache = make(map[string]string)
func doStuff(key string) {
//do-something-that-takes-a-long-time
cache[key] = value
return value
}
fun DoStuff(key string) {
if v, ok := cache[key]; ok {
return v
}
return doStuff(key)
}