Go中的动态函数调用

Go中的动态函数调用,go,first-class-functions,Go,First Class Functions,我试图动态调用返回不同类型的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 } 我想将函数doA或do

我试图动态调用返回不同类型的
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
}
我想将函数
doA
doB
作为参数传递给执行函数并对结果进行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 {})

有没有办法实现此动态调用?

这些函数的返回签名不同:
fn func()(接口{})
vs

您将收到一个编译器错误,因为您正在将一个具有不同签名的函数传递到
泛型
函数中。要解决此问题,可以将函数更改为返回
接口{}

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

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())
}

在围棋场上进行实验:

首先,让我指出
func()(interface{})
func()interface{}
的意思相同,因此我将使用较短的形式

传递类型为
func()接口{}
只要传递给它的函数的类型为
func()接口{}
,就可以编写一个接受
func()接口{}
参数的泛型函数,如下所示:

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)
}
您可以在活动的游乐场中试用此代码:

将函数作为
接口{}
如果要编写返回具体类型值的函数
doA
doB
,可以将所选函数作为
interface{}
类型的参数传递。然后,可以在运行时使用创建
func()接口{}

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)
}
运动场:


我也得出了这个结论,但我想保留doA()和doB()返回值的强类型。我想没有办法做到这一点?如果你让你的
Generic
函数接受
interface{}
类型的参数,你就可以做到这一点。请看我的扩展答案。虽然非常有趣/信息丰富,但“真正”的答案当然是“不要这样做”。只有在其他一切都失败的情况下才应该使用反射,它应该是你求助的最后手段,而不是你尝试的第一件事。另请注意,这是一个绝对零错误检查的OP示例,这是一个巨大的“不不,决不忽略错误”,例如来自
json.Encode
。如何做到这一点?fn:=“doA”泛型(os.Stdout,fn)将来剪切粘贴代码时请更加小心。你的代码片段甚至不是有效的Go(
struct B{
vs
typeb struct{
)在
Generic
定义中丢失了
。你还应该
gofmt
有问题的所有Go代码(例如,这将删除返回类型周围的多余括号,这只会使它更难阅读)。最后,提供play.golang.org链接对试图回答问题的人和想尝试/试验该问题的读者都非常有帮助;例如。