web-dev-qa-db-fra.com

Interface de Golang à struct

J'ai une fonction qui a un paramètre avec le type interface {}, quelque chose comme:

func LoadTemplate(templateData interface{}) {

Dans mon cas, templateData est une structure, mais chaque fois, elle a une structure différente. J'ai utilisé le type "interface {}" car cela me permet d'envoyer tout type de données.

J'utilise cette templateData pour envoyer les données au template:

err := tmpl.ExecuteTemplate(w, baseTemplateName, templateData)

Mais maintenant, je veux annexer de nouvelles données et je ne sais pas comment le faire, car le type "interface" ne me permet pas d’ajouter ou d’ajouter quoi que ce soit. 

J'ai essayé de convertir l'interface en structure, mais je ne sais pas comment ajouter des données à une structure avec une structure inconnue. 

Si j'utilise la fonction suivante, je peux voir les données de l'interface:

templateData = appendAssetsToTemplateData(templateData)

func appendAssetsToTemplateData(t interface{}) interface{} {
    switch reflect.TypeOf(t).Kind() {
    case reflect.Struct:
        fmt.Println("struct")
        s := reflect.ValueOf(t)
        fmt.Println(s)

        //create a new struct based on current interface data
    }

    return t
}

Toute idée de comment puis-je ajouter un enfant au paramètre d'interface initial (templateData)? Ou comment puis-je le transformer en une structure ou autre chose afin d'ajouter le nouvel enfant/données?

5
Pascut

Adrian a raison. Pour aller plus loin, vous ne pouvez rien faire avec les interfaces si vous connaissez le type qui implémente cette interface. L’interface vide, interface{} n’est pas vraiment une valeur "quoi que ce soit" comme elle est souvent mal comprise; c'est juste une interface qui est immédiatement satisfaite par tous les types.

Par conséquent, vous pouvez uniquement obtenir des valeurs ou créer une nouvelle "interface" avec des valeurs ajoutées en connaissant le type satisfaisant l'interface vide avant et après l'ajout.

Le plus près que vous puissiez faire ce que vous voulez, étant donné le typage statique, consiste à incorporer le type before dans le type after, de sorte que tout puisse toujours être consulté à la racine du type after. Ce qui suit illustre ceci.

https://play.golang.org/p/JdF7Uevlqp

package main

import (
    "fmt"
)

type Before struct {
    m map[string]string
}

type After struct {
    Before
    s []string
}

func contrivedAfter(b interface{}) interface{} {
    return After{b.(Before), []string{"new value"}}
}

func main() {
    b := Before{map[string]string{"some": "value"}}
    a := contrivedAfter(b).(After)
    fmt.Println(a.m)
    fmt.Println(a.s)
}

De plus, étant donné que les données que vous transmettez au modèle ne nécessitent pas de spécifier le type, vous pouvez utiliser une structure anonyme pour accomplir quelque chose de très similaire.

https://play.golang.org/p/3KUfHULR84 ​​

package main

import (
    "fmt"
)

type Before struct {
    m map[string]string
}

func contrivedAfter(b interface{}) interface{} {
    return struct{
        Before
        s []string
    }{b.(Before), []string{"new value"}}
}

func main() {
    b := Before{map[string]string{"some": "value"}}
    a := contrivedAfter(b)
    fmt.Println(a)
}
8
TheHerk

Vous ne pouvez pas ajouter des données de manière arbitraire à une structure. ils sont typés statiquement. Vous pouvez uniquement affecter des valeurs aux champs définis pour ce type de structure spécifique. Votre meilleur choix est probablement d’utiliser une variable map au lieu de structs pour cela.

4
Adrian

Non recommandé, mais vous pouvez créer des structures de manière dynamique à l'aide du paquet reflect.

Voici un exemple:

paquet principal

import (
    "encoding/json"
    "os"
    "reflect"
)

type S struct {
    Name string
}

type D struct {
    Pants bool
}

func main() {
    a := Combine(&S{"Bob"}, &D{true})
    json.NewEncoder(os.Stderr).Encode(a)
}

func Combine(v ...interface{}) interface{} {
    f := make([]reflect.StructField, len(v))
    for i, u := range v {
        f[i].Type = reflect.TypeOf(u)
        f[i].Anonymous = true
    }

    r := reflect.New(reflect.StructOf(f)).Elem()
    for i, u := range v {
        r.Field(i).Set(reflect.ValueOf(u))
    }
    return r.Addr().Interface()
}

Vous pouvez utiliser quelque chose comme la fonction Combine ci-dessus pour regrouper un nombre quelconque de structures. Malheureusement, à partir de documentation :

Actuellement, StructOf ne génère pas de méthodes d'encapsulation pour les champs incorporés. Cette limitation pourrait être levée dans une future version.

Ainsi, votre structure créée n'héritera pas des méthodes des types incorporés. Pourtant, peut-être qu'il fait ce dont vous avez besoin.

1
chowey