ホームページ > バックエンド開発 > Golang > レシーバーのタイプに関係なく、Go のインターフェースでメソッドを動的に呼び出す方法{}

レシーバーのタイプに関係なく、Go のインターフェースでメソッドを動的に呼び出す方法{}

Barbara Streisand
リリース: 2024-12-02 01:39:09
オリジナル
283 人が閲覧しました

How to Dynamically Call Methods on an interface{} in Go, Regardless of Receiver Type?

レシーバーのタイプに関係なく、インターフェイス上でメソッドを動的に呼び出す{}

Go のテンプレート システムの領域では、リフレクションが重要な役割を果たします。さまざまなレシーバー タイプを使用してインターフェース上でメソッドを動的に呼び出すときに問題が発生します。{}これは既知の型ではシームレスに機能しますが、データがインターフェース内でラップされている場合は失敗します。

問題ステートメント

次の点を考慮してください。コード:

type Test struct {
    Start string
}

func (t *Test) Finish() string {
    return t.Start + "finish"
}

func Pass(i interface{}) {
    _, ok := reflect.TypeOf(&i).MethodByName("Finish")
    if ok {
        fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0])
    } else {
        fmt.Println("Pass() fail")
    }
}

func main() {
    i := Test{Start: "start"}

    Pass(i)
    _, ok := reflect.TypeOf(&i).MethodByName("Finish")
    if ok {
        fmt.Println(reflect.ValueOf(&i).MethodByName("Finish").Call([]reflect.Value{})[0])
    } else {
        fmt.Println("main() fail")
    }
}
ログイン後にコピー

Observations

  • データが既知の型 (main()) の場合、動的メソッド呼び出しは成功します。
  • データがインターフェース(Pass())でラップされると失敗し、最初の実行時に「Pass() failed」を返します。 call.

問題

問題は、データがインターフェースにラップされている場合にそのアドレスにアクセスすることにあります。{}通常ポインターを指す &i の使用は、このシナリオでは機能しません。

解決策

これに対処するには、考えられるすべてのシナリオを処理する必要があります。

  1. インターフェース データを値として、メソッド レシーバーをメソッド レシーバとして使用します。 value:

    • データが値の場合は、そのデータへのポインタを作成します。
  2. interface{} data asポインターとしての値とメソッド レシーバー:

    • As上記では、データへのポインタを作成します。
  3. インターフェース データをポインタとして、メソッド レシーバを値として使用します。

    • データがポインタの場合、それが指す値を取得しますto.
  4. インターフェース データをポインタとして、メソッド レシーバをポインタとして使用します:

    • ポインタを使用します

実装

このロジックに基づいて、メソッドを動的に呼び出す一般化された関数を作成できます。

func CallMethod(i interface{}, methodName string) interface{} {
    // Handle all scenarios
    var ptr, value, finalMethod reflect.Value
    value = reflect.ValueOf(i)
    if value.Type().Kind() == reflect.Ptr {
        ptr = value
        value = ptr.Elem()
    } else {
        ptr = reflect.New(reflect.TypeOf(i))
        temp := ptr.Elem()
        temp.Set(value)
    }

    // Check for method on value and pointer
    method := value.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }
    method = ptr.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }

    // Call the method
    if (finalMethod.IsValid()) {
        return finalMethod.Call([]reflect.Value{})[0].Interface()
    }

    // Method not found
    return ""
}
ログイン後にコピー

この関数を使用すると、レシーバーのタイプやインターフェースに関係なくメソッドを動的に呼び出すことができるようになりました。{}ラッパー:

i := Test{Start: "start"}
j := Test{Start: "start2"}

fmt.Println(CallMethod(i, "Finish"))
// Output: startfinish
fmt.Println(CallMethod(&i, "Finish"))
// Output: startfinish
fmt.Println(CallMethod(i, "Another"))
// Output:
fmt.Println(CallMethod(&i, "Another"))
// Output: start2another
fmt.Println(CallMethod(j, "Finish"))
// Output: startfinish
fmt.Println(CallMethod(&j, "Finish"))
// Output: start2finish
fmt.Println(CallMethod(j, "Another"))
// Output:
fmt.Println(CallMethod(&j, "Another"))
// Output: start2another
ログイン後にコピー

以上がレシーバーのタイプに関係なく、Go のインターフェースでメソッドを動的に呼び出す方法{}の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート