ホームページ >バックエンド開発 >Golang >Go のインターフェイスでメソッドを動的に呼び出し、値レシーバとポインタ レシーバの両方を処理するにはどうすればよいですか?

Go のインターフェイスでメソッドを動的に呼び出し、値レシーバとポインタ レシーバの両方を処理するにはどうすればよいですか?

Susan Sarandon
Susan Sarandonオリジナル
2024-11-27 18:35:15284ブラウズ

How Can I Dynamically Invoke Methods on Interfaces in Go, Handling Both Value and Pointer Receivers?

インターフェイスでのメソッドの動的呼び出し

チャレンジ

テンプレート システムは Go のリフレクト パッケージに大きく依存しています。このパッケージを使用する場合、インターフェイス上でメソッドを動的に呼び出す際に問題が発生する可能性があります。この問題は、データ型がインターフェースとして保存される場合に明らかになります。

概念的な理解

インターフェースのメソッド呼び出しのダイナミクスを理解することが重要です。考慮すべきシナリオは 4 つあります。

  • 値レシーバー メソッドで値を保持するインターフェイス
  • 値レシーバー メソッドでポインターを保持するインターフェイス
  • 値を保持するインターフェイスポインタ レシーバ メソッドを使用する
  • ポインタ レシーバを使用してポインタを保持するインターフェイスmethod

リフレクションは、インターフェイスの基礎となるデータ値を決定するのに役立ちます。この情報を使用して、代替データ型を生成し、値受信メソッドとポインター受信メソッドを区別できます。

解決策

問題を解決するには、値とポインター表現の両方を作成する必要があります。データの:

value := reflect.ValueOf(data)
if value.Type().Kind() == reflect.Ptr {
    ptr = value
    value = ptr.Elem() // acquire value referenced by pointer
} else {
    ptr = reflect.New(reflect.TypeOf(i)) // create new pointer
    temp := ptr.Elem() // create variable to value of pointer
    temp.Set(value) // set value of variable to our passed in value
}

両方のデータ型が利用可能な場合、メソッドの存在のチェックは次のようになります。簡単:

var finalMethod reflect.Value
method := value.MethodByName(methodName)
if method.IsValid() {
    finalMethod = method
}
// check for method on pointer
method = ptr.MethodByName(methodName)
if method.IsValid() {
    finalMethod = method
}

if (finalMethod.IsValid()) {
    return finalMethod.Call([]reflect.Value{})[0].String()
}

このアプローチを採用すると、値またはポインター レシーバーとして定義されているかどうかに関係なく、任意のメソッドを効果的に動的に呼び出すことが可能になります。

デモ

package main

import (
    "fmt"
    "reflect"
)

type Test struct {
    Start string
}

// value receiver
func (t Test) Finish() string {
    return t.Start + "finish"
}

// pointer receiver
func (t *Test) Another() string {
    return t.Start + "another"
}

func CallMethod(i interface{}, methodName string) interface{} {
    var ptr reflect.Value
    var value reflect.Value
    var finalMethod reflect.Value

    value = reflect.ValueOf(i)

    // if we start with a pointer, we need to get value pointed to
    // if we start with a value, we need to get a pointer to that value
    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
    method := value.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }
    // check for method on pointer
    method = ptr.MethodByName(methodName)
    if method.IsValid() {
        finalMethod = method
    }

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

    // return or panic, method not found of either type
    return ""
}

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

    fmt.Println(CallMethod(i, "Finish"))
    fmt.Println(CallMethod(&i, "Finish"))
    fmt.Println(CallMethod(i, "Another"))
    fmt.Println(CallMethod(&i, "Another"))
    fmt.Println(CallMethod(j, "Finish"))
    fmt.Println(CallMethod(&j, "Finish"))
    fmt.Println(CallMethod(j, "Another"))
    fmt.Println(CallMethod(&j, "Another"))
}

出力:

startfinish
startfinish
<nil>
startanother
startfinish
startfinish
<nil>
startanother

以上がGo のインターフェイスでメソッドを動的に呼び出し、値レシーバとポインタ レシーバの両方を処理するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。