Maison >développement back-end >Golang >Comment appeler une fonction générique dans Go en passant un Type via réflexion

Comment appeler une fonction générique dans Go en passant un Type via réflexion

王林
王林avant
2024-02-05 22:21:031256parcourir

如何通过反射传递 Type 来调用 Go 中的泛型函数

Contenu de la question

J'ai une application Go qui reçoit différentes réponses JSON pour la même API, j'ai donc essayé d'écrire un unmarshaller personnalisé qui essaiera de traiter chaque réponse JSON possible jusqu'à ce qu'il trouve la bonne réponse.

Pour ce faire, j'ai créé une fonction générique pour désorganiser JSON, et je souhaite l'appeler en utilisant le type obtenu par réflexion.

Par exemple :

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "reflect"
)

type JSONHandler struct{}

func (handler *JSONHandler) Unmarshal(data []byte) error {
    t := reflect.TypeOf(*handler)

    for i := 0; i < t.NumField(); i++ {
        fieldType := t.Field(i)

        fmt.Printf("Trying to unmarshal: %s\n", fieldType.Name)
        result, err := unmarshal[fieldType](data) // fieldType (variable of type reflect.StructField) is not a typecompiler (NotAType)

        if err == nil {
            fmt.Printf("result: %s\n", result)
            break
        }
    }

    return nil
}

func unmarshal[T interface{}](data []byte) (*T, error) {
    obj := new(T)

    reader := bytes.NewReader(data)
    decoder := json.NewDecoder(reader)
    decoder.DisallowUnknownFields()

    err := decoder.Decode(obj)
    if err != nil {
        return nil, err
    }
    return obj, nil
}

type User struct {
    Username *string `json:"username,omitempty"`
    Password *string `json:"password,omitempty"`
}

type UserError struct {
    Error *string `json:"error,omitempty"`
    Code  *int    `json:"code,omitempty"`
}

type UserOrError struct {
    User      *User
    UserError *UserError
    JSONHandler
}

var userJson = `{ "username": "[email&#160;protected]", "password": "password" }`

func main() {
    user := new(UserOrError)
    result := user.Unmarshal([]byte(userJson)) // { User: something, UserError: nil }
}

Cependant, le compilateur Go m'a donné cette erreur : fieldType (reflect.StructField 类型的变量) 不是类型编译器 (NotAType).

Existe-t-il un moyen de transmettre des paramètres de réflexion à une fonction générique ?


Bonne réponse


Cette question dépasse la portée des fonctionnalités générales de Go.

Utilisez reflect.New pour créer une valeur étant donné le reflet.Type de cette valeur. p>

Go n'a pas d'héritage. La méthode Unmarshal dans la question fonctionne sur des types qui n'ont pas de champs. Corrigé en passant le "handler" à une fonction normale.

// Unmarshal unmarshals data to each field type in handler
// and returns the first successful result. The handler argument
// must be a pointer to a struct.
func Unmarshal(handler any, data []byte) (any, error) {
    t := reflect.TypeOf(handler).Elem()
    for i := 0; i < t.NumField(); i++ {
        fieldType := t.Field(i)
        v := reflect.New(fieldType.Type)
        decoder := json.NewDecoder(bytes.NewReader(data))
        decoder.DisallowUnknownFields()
        err := decoder.Decode(v.Interface())
        if err == nil {
            return v.Elem().Interface(), err
        }
    }
    return nil, errors.New("no matching type")
}

Utilisez la fonction Marshal comme ceci :

type UserOrError struct {
    User      *User
    UserError *UserError
}

var userJson = `{ "username": "<a href="https://www.php.cn/link/89fee0513b6668e555959f5dc23238e9" class="__cf_email__" data-cfemail="0e646166604e6b636f6762206d6163">[email&#160;protected]</a>", "password": "password" }`

result, err := Unmarshal(&UserOrError{}, []byte(userJson)) // { User: something, UserError: nil }
fmt.Printf("err=%v, result=%#v\n", err, result)

https://www.php.cn/link/c76e4b2fa54f8506719a5c0dc14c2eb9

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:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer