Suche…


Einführung

Strukturen sind Mengen verschiedener Variablen, die zusammengepackt sind. Die struct selbst ist nur ein Paket, das Variablen enthält und leicht zugänglich ist.

Anders als in C können Gos Strukturen mit Methoden verbunden werden. Es erlaubt ihnen auch, Schnittstellen zu implementieren. Dadurch ähneln die Strukturen von Go Objekten, aber sie fehlen (wahrscheinlich absichtlich) einige wichtige Funktionen, die in objektorientierten Sprachen bekannt sind, wie Vererbung.

Grundlegende Erklärung

Eine grundlegende Struktur wird wie folgt deklariert:

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

Jeder Wert wird als Feld bezeichnet. Die Felder werden normalerweise einzeln geschrieben, wobei der Name des Felds vor seinem Typ steht. Aufeinanderfolgende Felder desselben Typs können im obigen Beispiel als FirstName und LastName kombiniert werden.

Exportierte vs. nicht exportierte Felder (privat und öffentlich)

Strukturfelder, deren Namen mit einem Großbuchstaben beginnen, werden exportiert. Alle anderen Namen sind nicht exportiert.

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

Auf nicht enthaltene Felder kann nur durch Code innerhalb desselben Pakets zugegriffen werden. Wenn Sie also jemals auf ein Feld aus einem anderen Paket zugreifen, muss dessen Name mit einem Großbuchstaben beginnen.

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

Doch innerhalb der bank Paket können Sie sowohl Benutzer - ID und accessToken ohne Probleme zugreifen.

Das Paket bank könnte wie folgt realisiert werden:

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
}

Komposition und Einbettung

Die Zusammensetzung bietet eine Alternative zur Vererbung. Eine Struktur kann in der Deklaration einen anderen Typ nach Namen enthalten:

type Request struct {
    Resource string
}

type AuthenticatedRequest struct {
    Request
    Username, Password string
}

Im obigen Beispiel enthält AuthenticatedRequest vier öffentliche Mitglieder: Resource , Request , Username und Password .

Zusammengesetzte Strukturen können instanziiert und auf dieselbe Weise wie normale Strukturen verwendet werden:

func main() {
    ar := new(AuthenticatedRequest)
    ar.Resource = "example.com/request"
    ar.Username = "bob"
    ar.Password = "[email protected]"
    fmt.Printf("%#v", ar)
}

spiele es auf dem Spielplatz

Einbetten

Im vorherigen Beispiel ist Request ein eingebettetes Feld. Die Zusammensetzung kann auch durch Einbetten eines anderen Typs erreicht werden. Dies ist z. B. nützlich, um eine Struktur mit mehr Funktionen zu dekorieren. Wenn Sie beispielsweise mit dem Beispiel "Ressource" fortfahren, möchten wir eine Funktion, die den Inhalt des Felds "Resource" so formatiert, dass http:// oder https:// vorangestellt wird. Wir haben zwei Möglichkeiten: Erstellen Sie die neuen Methoden in AuthenticatedRequest oder binden Sie sie aus einer anderen Struktur ein:

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
}

Und jetzt könnte die Hauptfunktion Folgendes tun:

func main() {
    ar := new(AuthenticatedRequest)
    ar.Resource = "www.example.com/request"
    ar.Username = "bob"
    ar.Password = "[email protected]"

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

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

Sehen Sie sich das AuthenticatedRequest mit einer eingebetteten ResourceFormatter Struktur an.

Aber der Nachteil davon ist , dass Sie keine Objekte außerhalb Ihrer Komposition zugreifen können. ResourceFormatter kann also nicht über AuthenticatedRequest auf Mitglieder zugreifen.

spiele es auf dem Spielplatz

Methoden

Strukturmethoden sind Funktionen sehr ähnlich:

type User struct {
    name string
}

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

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

Der einzige Unterschied besteht in der Hinzufügung des Methodenempfängers. Sie kann entweder als Instanz des Typs oder als Zeiger auf eine Instanz des Typs deklariert werden. Da SetName() die Instanz verändert, muss der Empfänger ein Zeiger sein, um eine permanente Änderung in der Instanz zu bewirken.

Zum Beispiel:

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

Spielplatz gehen

Anonyme Struktur

Es ist möglich, eine anonyme Struktur zu erstellen:

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

Vollständiges Beispiel:

package main

import (
    "fmt"
)

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

spiele es auf dem Spielplatz

Stichworte

Strukturfeldern können Tags zugeordnet sein. Diese Tags können vom reflect Paket gelesen werden, um vom Entwickler benutzerdefinierte Informationen zu einem Feld zu erhalten.

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

Im obigen Beispiel werden die Tags verwendet, um die Schlüsselnamen zu ändern, die vom encoding/json Paket verwendet werden, wenn JSON gemarshallt oder das Marshallen aufgehoben wird.

Das Tag kann ein beliebiger Zeichenfolgenwert sein. Es wird jedoch empfohlen, durch Leerzeichen getrennte key:"value" -Paare:

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

Die struct-Tags, die mit dem Paket encoding/xml und encoding/json werden, werden in der Standard-Bibliothek verwendet.

Strukturkopien erstellen.

Eine Struktur kann einfach durch Zuweisung kopiert werden.

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

In obigem Fall sind 't' und 'u' jetzt separate Objekte (Strukturwerte).

Da T keine Referenztypen (Slices, Map, Channels) als Felder enthält, können t und u oben geändert werden, ohne sich gegenseitig zu beeinflussen.

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

Wenn T einen Referenztyp enthält, beispielsweise:

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

Dann würde eine einfache Kopie durch Zuweisung den Wert des Slice-Type-Felds in das neue Objekt kopieren. Dies würde dazu führen, dass zwei verschiedene Objekte auf dasselbe Slice-Objekt verweisen.

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

Da sich u und t über ihr Feld auf dasselbe Segment beziehen, würde das Aktualisieren eines Werts im Slice eines Objekts die Änderung im anderen Objekt widerspiegeln.

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

Daher muss besonders darauf geachtet werden, dass diese Referenztyp-Eigenschaft nicht zu unbeabsichtigtem Verhalten führt.

Um beispielsweise Objekte zu kopieren, kann eine explizite Kopie des Slice-Felds durchgeführt werden:

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

Ein Wert eines Strukturtyps kann mit einem Strukturliteral geschrieben werden, das Werte für seine Felder angibt.

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

Das obige Beispiel gibt jedes Feld in der richtigen Reihenfolge an. Was nicht sinnvoll ist, da sich Programmierer die genauen Felder in der richtigen Reihenfolge merken müssen. Häufiger kann eine Struktur durch Auflisten einiger oder aller Feldnamen und ihrer entsprechenden Werte initialisiert werden.

anim := gif.GIF{LoopCount: nframes}

Ausgelassene Felder werden für ihren Typ auf Null gesetzt.

Hinweis: Die beiden Formulare können nicht in demselben Literal gemischt werden.

Leere Struktur

Eine Struktur ist eine Folge benannter Elemente, genannt Felder, von denen jedes einen Namen und einen Typ hat. Leere Struktur hat keine Felder wie diese anonyme leere Struktur:

var s struct{}

Oder wie dieser benannte leere Strukturtyp:

type T struct{}

Das Interessante an der leeren Struktur ist, dass sie null ist (versuchen Sie den Go Playground ):

fmt.Println(unsafe.Sizeof(s))

Dies gibt 0 , sodass die leere Struktur selbst keinen Speicherplatz benötigt. Es ist also eine gute Option für das Beenden eines Kanals, wie (versuchen Sie es mit 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow