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

speel het op speelplaats

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 .

speel het op speelplaats

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

Ga spelen

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

speel het op speelplaats

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



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow