6

のさまざまなタイプを返す関数を動的に呼び出そうとしています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 {})

この動的呼び出しを実現する方法はありますか?

4

2 に答える 2

13

まず、func() (interface{})は と同じ意味なfunc() interface{}ので、短縮形を使用します。

タイプの関数を渡すfunc() interface{}

func() interface{}次のように、渡す関数が type である限り、引数を取る汎用関数を作成できます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)
}

ライブ プレイグラウンドでこのコードを試すことができます。

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

型の引数として関数を渡すinterface{}

関数を記述doAdoB、具体的に型指定された値を返す場合は、選択した関数を type の引数として渡すことができますinterface{}。次に、reflectパッケージ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

于 2015-09-19T21:49:34.037 に答える
0

これらの関数では、戻り値の署名が異なります: fn func() (interface {})vs. func doA() (A)andfunc doB() (B)

異なるシグネチャを持つ関数を関数に渡しているため、コンパイラ エラーが発生していますGeneric。この問題に対処するには、関数を return に変更します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

于 2015-09-19T21:40:13.163 に答える