Ricerca…


introduzione

Le strutture sono insiemi di varie variabili imballate insieme. La struct stessa è solo un pacchetto contenente variabili e rendendole facilmente accessibili.

A differenza di C, le strutture di Go possono avere metodi collegati a loro. Permette anche loro di implementare interfacce. Ciò rende le strutture di Go simili agli oggetti, ma sono (probabilmente intenzionalmente) mancanti di alcune delle principali funzionalità conosciute nei linguaggi orientati agli oggetti come l'ereditarietà.

Dichiarazione di base

Una struttura di base è dichiarata come segue:

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

Ogni valore è chiamato un campo. I campi sono solitamente scritti uno per riga, con il nome del campo che precede il suo tipo. Campi consecutivi dello stesso tipo possono essere combinati, come FirstName e LastName nell'esempio precedente.

Campi Esportati vs. Non Esportati (Privati ​​vs Pubblico)

Vengono esportati i campi Struct i cui nomi iniziano con una lettera maiuscola. Tutti gli altri nomi non sono stati esportati.

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

I campi non esportati possono essere raggiunti solo dal codice all'interno dello stesso pacchetto. Pertanto, se si accede sempre a un campo da un pacchetto diverso , il suo nome deve iniziare con una lettera maiuscola.

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

Tuttavia, dall'interno bank pacchetto bank , è possibile accedere a UserId e accessToken senza problemi.

La bank pacchetti potrebbe essere implementata in questo modo:

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
}

Composizione e incorporamento

La composizione fornisce un'alternativa all'eredità. Una struct può includere un altro tipo per nome nella sua dichiarazione:

type Request struct {
    Resource string
}

type AuthenticatedRequest struct {
    Request
    Username, Password string
}

Nell'esempio sopra, AuthenticatedRequest conterrà quattro membri pubblici: Resource , Request , Username e Password .

Le strutture composite possono essere istanziate e utilizzate allo stesso modo delle normali strutture:

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

giocarci sul campo da gioco

Incorporare

Nell'esempio precedente, Request è un campo incorporato. La composizione può anche essere ottenuta inserendo un tipo diverso. Questo è utile, ad esempio, per decorare una struttura con più funzionalità. Ad esempio, continuando con l'esempio di risorsa, vogliamo una funzione che formatta il contenuto del campo Risorsa per prefissarlo con http:// o https:// . Abbiamo due opzioni: crea i nuovi metodi su AuthenticatedRequest o incorporalo da una diversa struttura:

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
}

E ora la funzione principale potrebbe fare quanto segue:

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

Guarda che AuthenticatedRequest ha una struttura incorporata ResourceFormatter .

Ma il rovescio della medaglia è che non puoi accedere ad oggetti al di fuori della tua composizione. Quindi ResourceFormatter non può accedere ai membri da AuthenticatedRequest .

giocarci sul campo da gioco

metodi

I metodi di costruzione sono molto simili alle funzioni:

type User struct {
    name string
}

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

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

L'unica differenza è l'aggiunta del ricevitore del metodo. Può essere dichiarato come un'istanza del tipo o un puntatore a un'istanza del tipo. Poiché SetName() l'istanza, il ricevitore deve essere un puntatore per effettuare una modifica permanente nell'istanza.

Per esempio:

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())
}

Vai al parco giochi

Struttura anonima

È possibile creare una struttura anonima:

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

Esempio completo:

package main

import (
    "fmt"
)

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

giocarci sul campo da gioco

tag

I campi Struct possono avere tag associati a loro. Questi tag possono essere letti dal pacchetto reflect per ottenere informazioni personalizzate specificate su un campo dallo sviluppatore.

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

Nell'esempio precedente, i tag vengono utilizzati per modificare i nomi delle chiavi utilizzati dal pacchetto di encoding/json quando si effettua il marshalling o l'unmarshaling JSON.

Mentre il tag può essere qualsiasi valore di stringa, è consigliabile utilizzare la key:"value" separata dallo spazio key:"value" coppie key:"value" :

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

I tag struct utilizzati con il pacchetto encoding/xml e encoding/json sono usati in tutta la libarary standard.

Creare copie struct.

Una struttura può essere semplicemente copiata usando l'assegnazione.

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

Nel caso precedente, 't' e 'u' sono ora oggetti separati (valori struct).

Poiché T non contiene alcun tipo di riferimento (fette, mappa, canali) come i suoi campi, t u sopra possono essere modificati senza influire l'un l'altro.

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

Tuttavia, se T contiene un tipo di riferimento, ad esempio:

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

Quindi una semplice copia per assegnazione dovrebbe copiare il valore del campo del tipo di sezione anche nel nuovo oggetto. Ciò risulterebbe in due oggetti diversi che si riferiscono allo stesso oggetto fetta.

// 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

Poiché sia ​​u che t si riferiscono alla stessa porzione attraverso il loro campo xs, l'aggiornamento di un valore nella porzione di un oggetto rifletterebbe il cambiamento nell'altro.

// 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]

Pertanto, è necessario prestare la massima attenzione per garantire che questa proprietà del tipo di riferimento non produca comportamenti indesiderati.

Ad esempio, per copiare sopra oggetti, è possibile eseguire una copia esplicita del campo slice:

// 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 letterali

Un valore di un tipo di struct può essere scritto usando una struct literal che specifica i valori per i suoi campi.

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

L'esempio precedente specifica ogni campo nel giusto ordine. Che non è utile, perché i programmatori devono ricordare i campi esatti in ordine. Più spesso, una struttura può essere inizializzata elencando alcuni o tutti i nomi dei campi e i loro valori corrispondenti.

anim := gif.GIF{LoopCount: nframes}

I campi omessi sono impostati sul valore zero per il suo tipo.

Nota: le due forme non possono essere mescolate nello stesso valore letterale.

Struttura vuota

Una struct è una sequenza di elementi denominati, chiamati campi, ognuno dei quali ha un nome e un tipo. La struttura vuota non ha campi, come questa struttura anonima vuota:

var s struct{}

O come questo tipo di struct vuoto chiamato:

type T struct{}

La cosa interessante della struttura vuota è che, la sua dimensione è zero (prova The Go Playground ):

fmt.Println(unsafe.Sizeof(s))

Questo stampa 0 , quindi la struttura vuota non prende memoria. quindi è una buona opzione per uscire dal canale, come (prova The Go 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow