Go中的动态函数调用

Dynamic function call in Go

我正在尝试动态调用返回不同类型 struct 的函数。

例如,让我们看下面的代码。

struct A {
   Name string
   Value  int
}

struct B {
   Name1 string
   Name2 string
   Value   float
}

func doA() (A) {
   // some code returning A
}

func doB() (B) {
   // some code returning B
}

我想将函数 doAdoB 作为参数传递给将执行该函数并对结果进行 JSON 编码的通用函数。像下面这样:

func Generic(w io.Writer, fn func() (interface {}) {
    result := fn()
    json.NewEncoder(w).Encode(result)
}

但是当我这样做时:

Generic(w, doA)

我收到以下错误:

cannot use doA (type func() (A)) as type func() (interface {})

有没有办法实现这种动态调用?

您的 return 签名对于这些函数是不同的: fn func() (interface {}) 对比 func doA() (A)func doB() (B)

您收到编译器错误,因为您将具有不同签名的函数传递给 Generic 函数。要解决此问题,您可以将函数更改为 return interface{}.

这是一个如何做到这一点的例子,我使用匿名结构并打印出 return 值而不是序列化它们,但这同样适用于你的例子:

package main

import "fmt"

func doA() interface{} {
    return struct {
        Name  string
        Value int
    }{
        "something",
        5,
    }
}

func doB() interface{} {
    return struct {
        Name1 string
        Name2 string
        Value float64
    }{
        "something",
        "or other",
        5.3,
    }
}

func main() {
    fmt.Println("Hello, playground", doA(), doB())
}

在 Go Playground 中进行实验:http://play.golang.org/p/orrJw2XMW8

首先,让我说明 func() (interface{})func() interface{} 的意思相同,所以我将使用较短的形式。

正在传递 func() interface{}

类型的函数

您可以编写一个带有 func() interface{} 参数的通用函数,只要您传递给它的函数的类型为 func() interface{},就像这样:

type A struct {
    Name  string
    Value int
}

type B struct {
    Name1 string
    Name2 string
    Value float64
}

func doA() interface{} {
    return &A{"Cats", 10}
}

func doB() interface{} {
    return &B{"Cats", "Dogs", 10.0}
}

func Generic(w io.Writer, fn func() interface{}) {
    result := fn()
    json.NewEncoder(w).Encode(result)
}

您可以在现场 playground 中试用此代码:

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

将函数作为 interface{}

类型的参数传递

如果您想编写函数 doAdoB return 具体类型值,您可以将所选函数作为类型 interface{} 的参数传递。然后你可以使用 reflect package 在 运行 时创建一个 func() interface{}:

func Generic(w io.Writer, f interface{}) {
    fnValue := reflect.ValueOf(f)        // Make a concrete value.
    arguments := []reflect.Value{}       // Make an empty argument list.
    fnResults := fnValue.Call(arguments) // Assume we have a function. Call it.
    result := fnResults[0].Interface()   // Get the first result as interface{}.
    json.NewEncoder(w).Encode(result)    // JSON-encode the result.
}

更简洁:

func Generic(w io.Writer, fn interface{}) {
    result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
    json.NewEncoder(w).Encode(result)
}

完整的程序:

主要包

import (
    "encoding/json"
    "io"
    "os"
    "reflect"
)

type A struct {
    Name  string
    Value int
}

type B struct {
    Name1 string
    Name2 string
    Value float64
}

func doA() *A {
    return &A{"Cats", 10}
}

func doB() *B {
    return &B{"Cats", "Dogs", 10.0}
}

func Generic(w io.Writer, fn interface{}) {
    result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
    json.NewEncoder(w).Encode(result)
}

func main() {
    Generic(os.Stdout, doA)
    Generic(os.Stdout, doB)
}

现场游乐场:

http://play.golang.org/p/9M5Gr2HDRN