Recherche…


Introduction

Les structures sont des ensembles de différentes variables regroupés. La structure elle-même n'est qu'un package contenant des variables et les rendant facilement accessibles.

Contrairement à C, les structures de Go peuvent être associées à des méthodes. Cela leur permet également de mettre en œuvre des interfaces. Cela rend les structures de Go similaires aux objets, mais il manque (probablement intentionnellement) des fonctionnalités majeures connues dans les langages orientés objet tels que l'héritage.

Déclaration de base

Une structure de base est déclarée comme suit:

type User struct {
    FirstName, LastName string
    Email               string
    Age                 int
}

Chaque valeur est appelée un champ. Les champs sont généralement écrits un par ligne, le nom du champ précédant son type. Des champs consécutifs du même type peuvent être combinés, comme FirstName et LastName dans l'exemple ci-dessus.

Champs exportés et non exportés (privés et publics)

Les champs de structure dont les noms commencent par une lettre majuscule sont exportés. Tous les autres noms sont non exportés.

type Account struct {
    UserID      int    // exported
    accessToken string // unexported
}

Les champs non exportés ne sont accessibles que par code dans le même package. En tant que tel, si vous accédez à un champ depuis un autre package, son nom doit commencer par une lettre majuscule.

package main

import "bank"

func main() {
    var x = &bank.Account{
        UserID: 1,          // this works fine
        accessToken: "one", // this does not work, since accessToken is unexported
    }
}

Cependant, à partir du package de bank , vous pouvez accéder à UserId et à accessToken sans problème.

La bank paquets pourrait être implémentée comme ceci:

package bank

type Account struct {
    UserID int
    accessToken string
}

func ProcessUser(u *Account) {    
    u.accessToken = doSomething(u) // ProcessUser() can access u.accessToken because 
                                   // it's defined in the same package
}

Composition et enrobage

La composition offre une alternative à l'héritage. Une structure peut inclure un autre type par nom dans sa déclaration:

type Request struct {
    Resource string
}

type AuthenticatedRequest struct {
    Request
    Username, Password string
}

Dans l'exemple ci-dessus, AuthenticatedRequest contiendra quatre membres publics: Resource , Request , Username et Password .

Les structures composites peuvent être instanciées et utilisées de la même manière que les structures normales:

func main() {
    ar := new(AuthenticatedRequest)
    ar.Resource = "example.com/request"
    ar.Username = "bob"
    ar.Password = "P@ssw0rd"
    fmt.Printf("%#v", ar)
}

jouer sur le terrain de jeu

Enrobage

Dans l'exemple précédent, Request est un champ incorporé. La composition peut également être obtenue en intégrant un type différent. Ceci est utile, par exemple, pour décorer une structure avec plus de fonctionnalités. Par exemple, en continuant avec l'exemple Resource, nous voulons une fonction qui formate le contenu du champ Resource pour le préfixer avec http:// ou https:// . Nous avons deux options: créer les nouvelles méthodes sur AuthenticatedRequest ou l’ incorporer à partir d’une structure différente:

type ResourceFormatter struct {}

func(r *ResourceFormatter) FormatHTTP(resource string) string {
    return fmt.Sprintf("http://%s", resource)
}
func(r *ResourceFormatter) FormatHTTPS(resource string) string {
    return fmt.Sprintf("https://%s", resource)
}


type AuthenticatedRequest struct {
    Request
    Username, Password string
    ResourceFormatter
}

Et maintenant, la fonction principale pourrait faire ce qui suit:

func main() {
    ar := new(AuthenticatedRequest)
    ar.Resource = "www.example.com/request"
    ar.Username = "bob"
    ar.Password = "P@ssw0rd"

    println(ar.FormatHTTP(ar.Resource))
    println(ar.FormatHTTPS(ar.Resource))

    fmt.Printf("%#v", ar)
}

Regardez ce que AuthenticatedRequest a une structure incorporée ResourceFormatter .

Mais l'inconvénient est que vous ne pouvez pas accéder à des objets en dehors de votre composition. Donc, ResourceFormatter ne peut pas accéder aux membres de AuthenticatedRequest .

jouer sur le terrain de jeu

Les méthodes

Les méthodes Struct sont très similaires aux fonctions:

type User struct {
    name string
}

func (u User) Name() string {
    return u.name
}

func (u *User) SetName(newName string) {
    u.name = newName
}

La seule différence est l'ajout du récepteur de méthode. Il peut être déclaré soit comme une instance du type, soit comme un pointeur sur une instance du type. Étant donné que SetName() modifie l'instance, le récepteur doit être un pointeur afin d'effectuer un changement permanent dans l'instance.

Par exemple:

package main

import "fmt"

type User struct {
    name string
}

func (u User) Name() string {
    return u.name
}

func (u *User) SetName(newName string) {
    u.name = newName
}

func main() {
    var me User

    me.SetName("Slim Shady")
    fmt.Println("My name is", me.Name())
}

Aller au terrain de jeu

Structure anonyme

Il est possible de créer une structure anonyme:

data := struct {
    Number int 
    Text   string
} { 
    42, 
    "Hello world!",
}

Exemple complet:

package main

import (
    "fmt"
)

func main() {
    data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
    fmt.Printf("%+v\n", data)
}

jouer sur le terrain de jeu

Mots clés

Les champs de structure peuvent être associés à des balises. Ces balises peuvent être lues par le package de reflect pour obtenir des informations personnalisées spécifiées sur un champ par le développeur.

struct Account {
    Username      string `json:"username"`
    DisplayName   string `json:"display_name"`
    FavoriteColor string `json:"favorite_color,omitempty"`
}

Dans l'exemple ci-dessus, les balises sont utilisées pour modifier les noms de clé utilisés par le package encoding/json lors du marshaling ou de la suppression de JSON.

Bien que la balise puisse être n'importe quelle valeur de chaîne, il est conseillé d'utiliser des key:"value" séparées par des espaces key:"value" paires key:"value" :

struct StructName {
    FieldName int `package1:"customdata,moredata" package2:"info"`
}

Les balises struct utilisées avec le package encoding/xml et encoding/json sont utilisées dans la bibliothèque standard.

Faire des copies de structure

Une structure peut simplement être copiée en utilisant l'affectation.

type T struct {
    I int
    S string
}

// initialize a struct
t := T{1, "one"}

// make struct copy
u := t // u has its field values equal to t

if u == t { // true
    fmt.Println("u and t are equal") // Prints: "u and t are equal"
}

Dans le cas ci-dessus, 't' et «u» sont maintenant des objets séparés (valeurs de structure).

Comme T ne contient aucun type de référence (tranches, carte, canaux), ses champs, t et u ci-dessus peuvent être modifiés sans se toucher.

fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1

Cependant, si T contient un type de référence, par exemple:

type T struct {
    I  int
    S  string
    xs []int // a slice is a reference type
}

Ensuite, une simple copie par affectation copiera également la valeur du champ de type de tranche dans le nouvel objet. Cela se traduirait par deux objets différents faisant référence au même objet de tranche.

// initialize a struct
t := T{I: 1, S: "one", xs: []int{1, 2, 3}}

// make struct copy
u := t // u has its field values equal to t

Étant donné que les deux u et t se réfèrent à la même tranche dans leur champ, xs mettant à jour une valeur dans la tranche d'un objet reflète le changement dans l'autre.

// update a slice field in u
u.xs[1] = 500

fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 500 3], u.xs = [1 500 3]

Par conséquent, des précautions supplémentaires doivent être prises pour s'assurer que cette propriété de type référence ne produit pas de comportement involontaire.

Pour copier des objets ci-dessus par exemple, une copie explicite du champ de la tranche peut être effectuée:

// explicitly initialize u's slice field
u.xs = make([]int, len(t.xs))
// copy the slice values over from t
copy(u.xs, t.xs)

// updating slice value in u will not affect t
u.xs[1] = 500

fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 2 3], u.xs = [1 500 3]

Struct Literals

Une valeur d'un type de structure peut être écrite en utilisant un littéral struct qui spécifie des valeurs pour ses champs.

type Point struct { X, Y int }
p := Point{1, 2}

L'exemple ci-dessus spécifie chaque champ dans le bon ordre. Ce qui n'est pas utile, car les programmeurs doivent se rappeler les champs exacts dans l'ordre. Plus souvent, une structure peut être initialisée en listant certains ou tous les noms de champs et leurs valeurs correspondantes.

anim := gif.GIF{LoopCount: nframes}

Les champs omis sont définis sur la valeur zéro pour son type.

Remarque: Les deux formes ne peuvent pas être mélangées dans le même littéral.

Structure vide

Une structure est une séquence d'éléments nommés, appelés champs, chacun ayant un nom et un type. La structure vide n'a pas de champs, comme cette structure vide anonyme:

var s struct{}

Ou comme ce type de structure vide nommée:

type T struct{}

La chose intéressante à propos de la structure vide est que sa taille est zéro (essayez The Go Playground ):

fmt.Println(unsafe.Sizeof(s))

Ceci imprime 0 , donc la structure vide elle-même ne prend pas de mémoire. c'est donc une bonne option pour quitter canal, comme (essayez The Play Playground ):

package main

import (
    "fmt"
    "time"
)

func main() {
    done := make(chan struct{})
    go func() {
        time.Sleep(1 * time.Second)
        close(done)
    }()

    fmt.Println("Wait...")
    <-done
    fmt.Println("done.")
}



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow