Maison > développement back-end > Golang > Comment puis-je appeler dynamiquement des méthodes sur des interfaces dans Go, en gérant à la fois les récepteurs de valeurs et de pointeurs ?

Comment puis-je appeler dynamiquement des méthodes sur des interfaces dans Go, en gérant à la fois les récepteurs de valeurs et de pointeurs ?

Susan Sarandon
Libérer: 2024-11-27 18:35:15
original
284 Les gens l'ont consulté

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

Invocation dynamique de méthodes sur les interfaces

Challenge

Les systèmes de création de modèles s'appuient fortement sur le package Reflect de Go. Lorsque vous travaillez avec ce package, vous pourriez rencontrer des difficultés pour appeler dynamiquement des méthodes sur les interfaces. Ce problème devient apparent lorsque le type de données est stocké en tant qu'interface{}.

Compréhension conceptuelle

Comprendre la dynamique de l'invocation de méthode pour les interfaces est crucial. Il y a quatre scénarios à considérer :

  • Interface contenant une valeur avec une méthode de réception de valeur
  • Interface contenant un pointeur avec une méthode de réception de valeur
  • Interface contenant une valeur avec une méthode de récepteur de pointeur
  • Interface contenant un pointeur avec un récepteur de pointeur méthode

La réflexion peut aider à déterminer la valeur des données sous-jacentes d'une interface. Avec ces informations, on peut générer le type de données alternatif et différencier les méthodes de réception de valeur et de pointeur.

Solution

Pour résoudre le problème, il est nécessaire de créer à la fois une valeur et une représentation de pointeur. des données :

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
}
Copier après la connexion

Avec les deux types de données disponibles, vérifier la présence d'une méthode devient simple :

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()
}
Copier après la connexion

Par en adoptant cette approche, il devient possible d'invoquer efficacement n'importe quelle méthode de manière dynamique, qu'elle soit définie comme un récepteur de valeur ou de pointeur.

Démonstration

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"))
}
Copier après la connexion

Sortie :

startfinish
startfinish
<nil>
startanother
startfinish
startfinish
<nil>
startanother
Copier après la connexion

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!

source:php.cn
Déclaration de ce site Web
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
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal