Maison >développement back-end >Golang >Comment appeler dynamiquement des méthodes sur une interface {} dans Go, quel que soit le type de récepteur ?

Comment appeler dynamiquement des méthodes sur une interface {} dans Go, quel que soit le type de récepteur ?

Barbara Streisand
Barbara Streisandoriginal
2024-12-02 01:39:09249parcourir

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

Appel dynamique de méthodes sur une interface{} Quel que soit le type de récepteur

Dans le domaine des systèmes de modèles dans Go, la réflexion joue un rôle crucial. Un défi survient lors de l'appel dynamique de méthodes sur une interface {} avec différents types de récepteurs. Bien que cela fonctionne de manière transparente avec les types connus, cela échoue lorsque les données sont encapsulées dans une interface{}.

Énoncé du problème

Considérez le code suivant :

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

  • Lorsque les données sont connues tapez (main()), l'appel de méthode dynamique réussit.
  • Lorsque les données sont enveloppées dans une interface{} (Pass()), elle échoue, renvoyant "Pass() fail" au premier appel.

Le problème

Le problème réside dans l'accès à l'adresse des données lorsqu'elles sont enveloppées dans une interface{}. L'utilisation de &i, qui pointe généralement vers un pointeur, ne fonctionne pas dans ce scénario.

Solution

Pour résoudre ce problème, nous devons gérer tous les scénarios possibles :

  1. données d'interface{} en tant que récepteur de valeur et de méthode en tant que valeur :

    • Si les données sont une valeur, créez un pointeur vers elle.
  2. interface{} données comme un récepteur de valeur et de méthode comme pointeur :

    • Comme ci-dessus, créez un pointeur vers le data.
  3. interface{} données en tant que pointeur et récepteur de méthode en tant que valeur :

    • Si les données sont un pointeur, récupérez la valeur vers laquelle il pointe.
  4. interface{} les données comme pointeur et le récepteur de méthode comme pointeur :

    • Utilisez le pointeur directement.

Mise en œuvre

Sur la base de cette logique, nous pouvons créer une fonction généralisée pour appeler des méthodes dynamiquement :

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 ""
}

Avec cette fonction, nous pouvons désormais appeler des méthodes de manière dynamique quels que soient le type de récepteur et l'interface{} wrapper :

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn