在 Go 中将任意函数作为参数传递

我正在尝试扩展我对 Go 函数指针的了解,并且我有一个关于在 Go 中将函数作为参数传递的可能性和不可能的问题。


假设我想编写一个decorator()可以包装任何现有函数的函数。为简单起见,让我们将其限制为只接受一个参数并只返回一个值的函数。


如果我编写一个接受func(interface{}) interface{}作为参数的装饰器,只要我传入的函数也接受/返回一个interface{}类型,它就会隐式工作(请参阅 参考资料funcA)。


我的问题是——有没有办法将现有的类型函数转换为func(string) string类型,func(interface{}) interface{}以便它也可以传递给装饰器函数,而无需将其包装在新的匿名函数中(请参阅 参考资料funcB)?


package main


import (

    "fmt"

)


func decorate(inner func(interface{}) interface{}, args interface{}) interface {} {

    fmt.Println("Before inner")

    result := inner(args)

    fmt.Println("After inner")

    return result

}


func funcA(arg interface{}) interface{} {

    fmt.Print("Inside A, with arg: ")

    fmt.Println(arg)

    return "This is A's return value"

}


func funcB(arg string) string {

    fmt.Print("Inside B, with arg: ")

    fmt.Println(arg)

    return "This is B's return value"

}


func main() {

    

    // This one works. Output is:

    //

    //   Before inner

    //   Inside A, with arg: (This is A's argument)

    //   After inner

    //   This is A's return value

    //

    fmt.Println(decorate(funcA, "(This is A's argument)"))

    

    // This doesn't work. But can it?

    //fmt.Println(decorate(funcB, "(This is B's argument)"))

}


胡子哥哥
浏览 147回答 4
4回答

幕布斯7119047

这是不可能的。原因之一是传递参数的机制因函数而异,使用interface{}arg 并不意味着“接受任何东西”。例如,一个以结构为参数的函数将接收该结构的每个成员,但一个以包含该结构的 interface{} 的函数将接收两个字,一个包含结构的类型,另一个包含指向它。因此,在不使用泛型的情况下,实现这一点的唯一方法是使用适配器函数。

森林海

使用反射包来处理具有任意参数和结果类型的函数。func decorate(inner interface{}, args interface{}) interface{} {    fmt.Println("Before inner")    result := reflect.ValueOf(inner).Call([]reflect.Value{reflect.ValueOf(args)})    fmt.Println("After inner")    return result[0].Interface()}在操场上运行代码。与decorate问题中的函数一样,此答案中的函数假定一个参数和一个结果。必须修改该函数以处理其他函数类型。OP 应考虑问题中提出的匿名包装函数与此处使用反射包之间的权衡。通过反射 API 调用函数比通过匿名包装器调用函数慢。反射 API 也失去了类型安全性。匿名包装函数增加了冗长性。

一只斗牛犬

作为记录,随着 Go 1.18 和泛型的引入,该decorator功能变得几乎微不足道。您可以这样声明类型约束:type UnaryFunc[T any] interface {    func(T) T}约束本身被参数化T以允许接受和返回任意类型的一元函数。然后在decorate函数中使用类型参数实例化约束。签名变为:decorate[T any, F UnaryFunc[T]](inner F, arg T) T多亏了类型推断,您可以只将具体参数传递给函数,并且两者T都是F明确的。没有命名约束的示例替代方案:// accept and return Tdecorate[T any](inner func(T) T, arg T) T// only return Tdecorate[T any](inner func() T) T// return T and errordecorate[T any](inner func(T) (T, error), arg T) (T, error)// N-ary functiondecorate[T, U any](inner func(T, U) (T, error), argt T, argu U) (T, error)明显的限制是接口约束UnaryFunc只指定了只接受和返回一个 arg 类型的函数T。你不能这样做,因为接口约束的类型集可能包括支持相同操作的类型——并且使用一个 arg 调用与使用 N 个 args 调用不兼容。完整程序:package mainimport (    "fmt")type UnaryFunc[T any] interface {    func(T) T}func decorate[T any, F UnaryFunc[T]](inner F, arg T) T {    fmt.Println("before inner")    result := inner(arg)    fmt.Println("after inner")    return result}func funcA(arg int) int {    fmt.Println("inside A with:", arg)    return arg}func funcB(arg string) string {    fmt.Println("inside B with:", arg)    return arg}func main() {    // this works    decorate(funcA, 200)        // this also works    decorate(funcB, "Func B")}游乐场:https ://go.dev/play/p/3q01NiiWsve

qq_笑_17

有没有办法将 func(string) string 类型的现有函数转换为 func(interface{}) interface{} 类型,以便它也可以传递给装饰器函数,而无需将其包装在新的匿名函数中(见 funcB)?不,就这么简单:不。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go