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 = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
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 = "P@ssw0rd"
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.
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())
}
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)
}
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.")
}