【问题标题】:Invoke a function which is received as an interface variable in golang调用一个函数,该函数在 golang 中作为接口变量接收
【发布时间】:2016-06-15 16:37:05
【问题描述】:

我有一个类似下面的代码

package main

import "fmt"

func PrintThis(arg string) {
    fmt.Printf("I'm printing %s", arg)
}

func PrintThisAndThat(arg1, arg2 string) {
    fmt.Printf("Now printing %s and %s", arg1, arg2)
}

func Invoke(fn interface{}, args ...string) {
    //fn(args...)
}

func main() {
    Invoke(PrintThis, "foo")
    Invoke(PrintThisAndThat, "foo", "bar")
}

这不是实际的生产代码,但这是一个简化版本。

问题:- 如果我取消注释 //fn(args...) 行,我会收到编译错误 prog.go:14: cannot call non-function fn (type interface {})

如何执行作为 Invoke() 函数的参数接收的函数?

实现这一目标的正确方法是什么?

【问题讨论】:

    标签: generics reflection go interface


    【解决方案1】:

    您需要将参数fn 声明为函数,而不是interface{}fn(args...) 应该如何为PrintThis 工作,有一个参数,以及PrintThisAndThat,有两个参数?您需要决定要接收哪种类型的函数并将其声明为参数。

    【讨论】:

      【解决方案2】:

      你可以像http://play.golang.org/p/opotbIGdrA这样使用类型开关

      package main
      
      import "fmt"
      
      func PrintThis(arg string) {
          fmt.Printf("I'm printing %s", arg)
      }
      
      func PrintThisAndThat(arg1, arg2 string) {
          fmt.Printf("Now printing %s and %s", arg1, arg2)
      }
      
      func Invoke(fn interface{}, args ...string) {
          switch m := fn.(type) {
          case func(string):
              m(args[0])
          case func(string, string):
              m(args[0], args[1])
          default:
      
          }
      }
      
      func main() {
          Invoke(PrintThis, "foo")
          Invoke(PrintThisAndThat, "foo", "bar")
      }
      

      但是您需要知道将传递哪些函数才能使其正常工作。

      顺便说一句,您可以使用 ...string 而不是多个字符串参数将 PrintThis 转换为可变参数函数。

      【讨论】:

      • 有没有办法扩展这种方法来处理任何类型的函数?为简单起见,我只展示了两个函数。类似于m := fn.(reflect.TypeOf(fn))
      • @ManuViswam 可能有,我在移动设备上,所以很难玩,但这有点难,因为它是强类型的,所以编译器想知道编译时的内容。无论如何,你为什么要使用这种方法,必须有更好的方法来做到这一点。
      • 我正在为测试运行器编写插件。测试核心将向您发送一个函数对象及其参数,我必须接收它并执行该函数。我正在尝试为此编写一个通用方法。
      • @ManuViswam 查看 jims 答案可能更适合那个,我以为你知道正在传递的函数。我建议阅读blog.golang.org/laws-of-reflection,因为反射可能很危险
      【解决方案3】:

      您使用reflect.ValueCallCallSlice 方法将其作为函数调用。与所有 reflect.Value 方法一样,这种恐慌是 fn 是错误的类型。

      func Invoke(fn interface{}, args ...string) {
          v := reflect.ValueOf(fn)
          rargs := make([]reflect.Value, len(args))
          for i, a := range args {
              rargs[i] = reflect.ValueOf(a)
          }
          v.Call(rargs)
      }
      

      http://play.golang.org/p/xGmNLDcLL_

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-20
        • 2019-02-19
        • 1970-01-01
        • 2021-05-19
        • 2021-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多