GO 中的函数包装器

pla*_*zma 5 function wrapper go

我需要一个函数包装器,它将接受一个函数并返回它的包装器版本。我试图实现的是在函数执行之前和之后注入一些代码

func funcWrapper(myFunc interface{}){
    fmt.Println("Before")
    //call myFunc
    fmt.Println("After")
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 5

如果您知道函数的签名,则可以创建一个函数,该函数接受该函数类型的函数值,并返回另一个相同类型的函数值。您可以使用一个函数文字来执行您想要添加的额外功能,并在适当的时候调用传递的函数。

例如,假设我们有这个功能:

func myfunc(i int) int {
    fmt.Println("myfunc called with", i)
    return i * 2
}
Run Code Online (Sandbox Code Playgroud)

一个接受 anint并返回一个int(其输入数字的两倍)的函数。

这是一个可能的包装器,它在调用它之前和之后通过记录对其进行“注释”,还记录其输入和返回值:

func wrap(f func(i int) int) func(i int) int {
    return func(i int) (ret int) {
        fmt.Println("Before, i =", i)
        ret = f(i)
        fmt.Println("After, ret =", ret)
        return
    }
}
Run Code Online (Sandbox Code Playgroud)

示例测试:

wf := wrap(myfunc)
ret := wf(2)
fmt.Println("Returned:", ret)
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

Before, i = 2
myfunc called with 2
After, ret = 4
Returned: 4
Run Code Online (Sandbox Code Playgroud)

由于 Go 不支持泛型,因此您必须为要支持的每种不同的函数类型执行此操作。或者,您可以尝试编写一个通用的解决方案,reflect.MakeFunc()如您在这个问题中所见:Wrapper for任意函数在 Go 中,使用它会很痛苦。

如果你想支持多种函数类型,最好是为每个不同的函数类型创建一个单独的包装器,这样每个函数类型都可以有正确的返回类型(具有正确参数和结果类型的函数类型)。如果您还想支持没有参数和返回类型的包装函数,它可能看起来像这样:

func wrap(f func()) func() {
    return func() {
        fmt.Println("Before func()")
        f2()
        fmt.Println("After func()")
    }
}

func wrapInt2Int(f func(i int) int) func(i int) int {
    return func(i int) (ret int) {
        fmt.Println("Before func(i int) (ret int), i =", i)
        ret = f(i)
        fmt.Println("After func(i int) (ret int), ret =", ret)
        return
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在wrap()如下所示的单个函数中执行此操作,但它的缺点(类型安全性较低,更难使用)超过了其优点,因此我建议不要这样做,我只会为不同的函数类型创建单独的包装函数。

让我们也支持包装一个没有参数和返回类型的函数:

func myfunc2() {
    fmt.Println("myfunc2 called")
}
Run Code Online (Sandbox Code Playgroud)

包装函数:

func wrap(f interface{}) interface{} {
    switch f2 := f.(type) {
    case func(i int) (ret int):
        return func(i int) (ret int) {
            fmt.Println("Before func(i int) (ret int), i =", i)
            ret = f2(i)
            fmt.Println("After func(i int) (ret int), ret =", ret)
            return
        }
    case func():
        return func() {
            fmt.Println("Before func()")
            f2()
            fmt.Println("After func()")
        }
    }
    return nil
}
Run Code Online (Sandbox Code Playgroud)

测试它:

wf := wrap(myfunc).(func(int) int)
ret := wf(2)
fmt.Println("Returned:", ret)

wf2 := wrap(myfunc2).(func())
wf2()
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试这个):

Before func(i int) (ret int), i = 2
myfunc called with 2
After func(i int) (ret int), ret = 4
Returned: 4
Before func()
myfunc2 called
After func()
Run Code Online (Sandbox Code Playgroud)

由于 Go 中没有泛型,这个解决方案只能有一个 return type interface{},并且在使用它时,它的返回值必须手动“转换”,类型断言为您期望它返回的函数类型(例如wf2 := wrap(myfunc2).(func()))。