英文:
Dynamic function call in Go
问题
我正在尝试动态调用返回不同类型的struct
的函数。
例如,让我们看一下以下代码。
struct A {
Name string
Value int
}
struct B {
Name1 string
Name2 string
Value float
}
func doA() (A) {
// 一些返回A的代码
}
func doB() (B) {
// 一些返回B的代码
}
我想将函数doA
或doB
作为参数传递给一个通用函数,该函数将执行该函数并对结果进行JSON编码。就像下面这样:
func Generic(w io.Writer, fn func() (interface {}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
但是当我这样做时:
Generic(w, doA)
我得到以下错误:
无法将doA(类型为func()(A))用作类型为func()(interface {})的参数
有没有办法实现这种动态调用?
英文:
I'm trying to dynamically call functions returning different types of struct
.
For example, let's take the following code.
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
}
I would like to pass either the function doA
or doB
as an argument to a generic function that would execute the function and JSON-encode the result. Like the following:
func Generic(w io.Writer, fn func() (interface {}) {
result := fn()
json.NewEncoder(w).Encode(result)
}
But when I do:
Generic(w, doA)
I get the following error:
cannot use doA (type func() (A)) as type func() (interface {})
Is there a way to achieve this dynamic call?
答案1
得分: 15
首先,让我说明一下 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{}
的参数传递
如果你想编写返回具体类型值的函数 doA
和 doB
,你可以将所选函数作为类型为 interface{}
的参数传递。然后,你可以使用 reflect
包 在运行时创建一个 func() interface{}
:
func Generic(w io.Writer, f interface{}) {
fnValue := reflect.ValueOf(f) // 创建一个具体值。
arguments := []reflect.Value{} // 创建一个空的参数列表。
fnResults := fnValue.Call(arguments) // 假设我们有一个函数。调用它。
result := fnResults[0].Interface() // 将第一个结果作为 interface{} 获取。
json.NewEncoder(w).Encode(result) // 对结果进行 JSON 编码。
}
更简洁的写法:
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
完整的程序:
package main
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)
}
实时 playground:
http://play.golang.org/p/9M5Gr2HDRN
英文:
First, let me remark that func() (interface{})
means the same thing as func() interface{}
, so I'll use the shorter form.
Passing a function of type func() interface{}
You can write a generic function that takes a func() interface{}
argument as long as the function that you pass to it has type func() interface{}
, like this:
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)
}
You can try out this code in a live playground:
http://play.golang.org/p/JJeww9zNhE
Passing a function as an argument of type interface{}
If you want to write functions doA
and doB
that return concretely typed values, you can pass the chosen function as an argument of type interface{}
. Then you can use the reflect
package to make a func() interface{}
at run-time:
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.
}
More concisely:
func Generic(w io.Writer, fn interface{}) {
result := reflect.ValueOf(fn).Call([]reflect.Value{})[0].Interface()
json.NewEncoder(w).Encode(result)
}
Complete program:
package main
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)
}
Live playground:
答案2
得分: 0
你的返回签名与这些函数不同:
fn func() (interface {})
与 func doA() (A)
和 func doB() (B)
你得到了一个编译器错误,因为你将一个具有不同签名的函数传递给了你的 Generic
函数。为了解决这个问题,你可以将你的函数改为返回 interface{}
。
这是一个示例,我使用匿名结构体并将返回值打印出来,而不是将它们序列化,但对于你的示例同样适用:
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
英文:
Your return signature is different for these functions:
fn func() (interface {})
vs. func doA() (A)
and func doB() (B)
You are getting a compiler error because you are passing a function with a different signature into your Generic
function. To address this issue you can change your functions to return interface{}
.
This is an example of how to do that, I am using anonymous structs and printing the return value out rather than serializing them but this applies just the same to your example:
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())
}
Experiment with this in the Go Playground: http://play.golang.org/p/orrJw2XMW8
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论