Zoeken…
Invoering
Structs zijn sets van verschillende variabelen die samen zijn verpakt. De structuur zelf is slechts een pakket dat variabelen bevat en deze gemakkelijk toegankelijk maakt.
Anders dan in C, kunnen aan de structuren van Go methoden zijn gekoppeld. Het stelt hen ook in staat om interfaces te implementeren. Dat maakt de structuren van Go vergelijkbaar met objecten, maar ze missen (waarschijnlijk opzettelijk) enkele belangrijke functies die bekend zijn in objectgeoriënteerde talen zoals overerving.
Basisverklaring
Een basisstructuur wordt als volgt verklaard:
type User struct {
FirstName, LastName string
Email string
Age int
}
Elke waarde wordt een veld genoemd. Velden worden meestal één per regel geschreven, waarbij de naam van het veld het type voorafgaat. Opeenvolgende velden van hetzelfde type kunnen worden gecombineerd, zoals FirstName
en LastName
in het bovenstaande voorbeeld.
Geëxporteerde velden versus niet-geïmporteerde velden (privé versus openbaar)
Struct-velden waarvan de naam begint met een hoofdletter, worden geëxporteerd. Alle andere namen zijn niet-geëxporteerd.
type Account struct {
UserID int // exported
accessToken string // unexported
}
Niet-geïmporteerde velden zijn alleen toegankelijk via code binnen hetzelfde pakket. Als u dus ooit toegang krijgt tot een veld uit een ander pakket, moet de naam beginnen met een hoofdletter.
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
}
}
Echter, uit binnen de bank
-pakket, hebt u toegang tot zowel de gebruikers-ID en accessToken zonder kwestie.
Het pakket bank
zou kunnen worden uitgevoerd als volgt:
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
}
Samenstelling en insluiting
Samenstelling biedt een alternatief voor overerving. Een struct kan een ander type op naam in zijn verklaring opnemen:
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
In het bovenstaande voorbeeld bevat AuthenticatedRequest
vier openbare leden: Resource
, Request
, Username
en Password
.
Composietstructuren kunnen worden geïnstantieerd en op dezelfde manier worden gebruikt als normale structs:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
Embedding
In het vorige voorbeeld is Request
een ingebed veld. Samenstelling kan ook worden bereikt door een ander type in te sluiten. Dit is bijvoorbeeld handig om een Struct met meer functionaliteit te decoreren. Als we bijvoorbeeld doorgaan met het voorbeeld Bron, willen we dat een functie die de inhoud van het veld Bron opmaakt, deze voorafvoegt met http://
of https://
. We hebben twee opties: maak de nieuwe methoden op AuthenticatedRequest of sluit deze in vanuit een andere structuur:
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
}
En nu zou de hoofdfunctie het volgende kunnen doen:
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)
}
Kijk dat de AuthenticatedRequest
met een ingesloten structuur van ResourceFormatter
.
Maar het nadeel hiervan is dat je geen toegang hebt tot objecten buiten je compositie. Dus ResourceFormatter
heeft geen toegang tot leden van AuthenticatedRequest
.
methoden
Struct-methoden lijken erg op functies:
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
Het enige verschil is de toevoeging van de methode-ontvanger. Het kan worden gedeclareerd als een instantie van het type of een verwijzing naar een instantie van het type. Aangezien SetName()
de instantie muteert, moet de ontvanger een pointer zijn om een permanente wijziging in de instantie te kunnen uitvoeren.
Bijvoorbeeld:
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())
}
Anonieme struct
Het is mogelijk om een anonieme struct te maken:
data := struct {
Number int
Text string
} {
42,
"Hello world!",
}
Volledig voorbeeld:
package main
import (
"fmt"
)
func main() {
data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
fmt.Printf("%+v\n", data)
}
Tags
Aan structuurvelden kunnen tags zijn gekoppeld. Deze tags kunnen door het reflect
pakket worden gelezen om door de ontwikkelaar specifieke informatie over een veld te krijgen.
struct Account {
Username string `json:"username"`
DisplayName string `json:"display_name"`
FavoriteColor string `json:"favorite_color,omitempty"`
}
In het bovenstaande voorbeeld worden de tags gebruikt om de sleutelnamen te wijzigen die worden gebruikt door het encoding/json
pakket bij het in- of uitademen van JSON.
Hoewel de tag elke tekenreekswaarde kan zijn, wordt het als beste praktijk beschouwd om een door spaties gescheiden key:"value"
-paren:
struct StructName {
FieldName int `package1:"customdata,moredata" package2:"info"`
}
De struct-tags die worden gebruikt met de encoding/xml
en encoding/json
pakket worden overal in de standaard libarary gebruikt.
Struct kopieën maken.
Een struct kan eenvoudig met opdracht worden gekopieerd.
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 bovenstaand geval zijn 't'
en 'u' nu afzonderlijke objecten (struct-waarden).
Aangezien T
geen referentietypes (segmenten, kaart, kanalen) bevat omdat de velden ervan kunnen t
en u
hierboven worden gewijzigd zonder elkaar te beïnvloeden.
fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1
Als T
een referentietype bevat, bijvoorbeeld:
type T struct {
I int
S string
xs []int // a slice is a reference type
}
Dan kopieert een eenvoudige kopie door toewijzing de waarde van het segmenttype ook naar het nieuwe object. Dit zou resulteren in twee verschillende objecten die verwijzen naar hetzelfde segmentobject.
// 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
Omdat zowel u als t naar hetzelfde segment verwijzen via hun veld x, zou het bijwerken van een waarde in het segment van het ene object de verandering in het andere weerspiegelen.
// 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]
Daarom moet er extra op worden gelet dat deze eigenschap van het referentietype geen onbedoeld gedrag veroorzaakt.
Om bijvoorbeeld bovenstaande objecten te kopiëren, kan een expliciete kopie van het segmentveld worden uitgevoerd:
// 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
Een waarde van een struct-type kan worden geschreven met een letterlijke struct die waarden voor zijn velden specificeert.
type Point struct { X, Y int }
p := Point{1, 2}
Het bovenstaande voorbeeld geeft elk veld in de juiste volgorde aan. Dat is niet handig, omdat programmeurs de exacte velden in volgorde moeten onthouden. Vaker kan een struct worden geïnitialiseerd door enkele of alle veldnamen en hun bijbehorende waarden op te sommen.
anim := gif.GIF{LoopCount: nframes}
Weggelaten velden worden ingesteld op de nulwaarde voor het type.
Opmerking: de twee vormen kunnen niet in dezelfde letterlijke letter worden gemengd.
Leeg struct
Een struct is een reeks benoemde elementen, velden genoemd, die elk een naam en een type hebben. Lege struct heeft geen velden, zoals deze anonieme lege struct:
var s struct{}
Of zoals dit met de naam lege struct type:
type T struct{}
Het interessante van de lege structuur is dat de grootte nul is (probeer The Go Playground ):
fmt.Println(unsafe.Sizeof(s))
Dit drukt 0
, dus de lege structuur zelf neemt geen geheugen in beslag. dus het is een goede optie voor stoppen met kanaal, zoals (probeer 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.")
}