Szukaj…


Wprowadzenie

Struktury są zestawami różnych zmiennych spakowanych razem. Sama struktura jest tylko pakietem zawierającym zmienne i ułatwiającym ich dostęp.

W przeciwieństwie do C, struktury Go mogą mieć dołączone metody. Pozwala im także na implementację interfejsów. To sprawia, że struktury Go są podobne do obiektów, ale brakuje im (prawdopodobnie celowo) niektórych głównych funkcji znanych w językach obiektowych, takich jak dziedziczenie.

Podstawowa deklaracja

Podstawową strukturę deklaruje się następująco:

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

Każda wartość nazywana jest polem. Pola są zwykle zapisywane po jednym w wierszu, a nazwa pola poprzedza jego typ. Kolejne pola tego samego typu mogą być łączone, jak FirstName i LastName w powyższym przykładzie.

Pola wyeksportowane a niewyportowane (prywatne vs publiczne)

Pola struktur, których nazwy rozpoczynają się od dużej litery, są eksportowane. Wszystkie pozostałe nazwy nie są importowane.

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

Do niewyportowanych pól można uzyskać dostęp tylko za pomocą kodu w tym samym pakiecie. W związku z tym, jeśli kiedykolwiek uzyskujesz dostęp do pola z innego pakietu, jego nazwa musi zaczynać się wielką literą.

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

Jednak z pakietu bank można bez problemu uzyskać dostęp zarówno do UserId, jak i accessToken.

bank pakietów można zaimplementować w następujący sposób:

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
}

Skład i osadzanie

Kompozycja stanowi alternatywę dla dziedziczenia. Struktur może zawierać w deklaracji inny typ według nazwy:

type Request struct {
    Resource string
}

type AuthenticatedRequest struct {
    Request
    Username, Password string
}

W powyższym przykładzie Request AuthenticatedRequest będzie zawierać czterech członków publicznych: Resource , Request , nazwę Username i Password .

Struktury złożone mogą być tworzone i używane w taki sam sposób, jak zwykłe struktury:

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

zagraj na placu zabaw

Osadzanie

W poprzednim przykładzie Request jest polem osadzonym. Kompozycję można również osiągnąć poprzez osadzenie innego typu. Jest to przydatne na przykład do dekorowania Structa większą funkcjonalnością. Na przykład, kontynuując przykład z zasobem, chcemy, aby funkcja formatująca zawartość pola zasobu poprzedzała go http:// lub https:// . Mamy dwie opcje: utwórz nowe metody w AuthenticatedRequest lub osadz ją z innej struktury:

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
}

A teraz główna funkcja może wykonywać następujące czynności:

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

Spójrz, że AuthenticatedRequest z osadzoną strukturą ResourceFormatter .

Ale wadą jest to, że nie można uzyskać dostępu do obiektów poza kompozycją. Dlatego ResourceFormatter nie może uzyskać dostępu do członków z AuthenticatedRequest .

zagraj na placu zabaw

Metody

Metody struktur są bardzo podobne do funkcji:

type User struct {
    name string
}

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

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

Jedyną różnicą jest dodanie odbiornika metody. Może być zadeklarowany jako instancja typu lub wskaźnik do instancji typu. Ponieważ SetName() mutuje instancję, odbiornik musi być wskaźnikiem, aby spowodować trwałą zmianę w instancji.

Na przykład:

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

Idź na plac zabaw

Anonimowa struktura

Możliwe jest utworzenie anonimowej struktury:

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

Pełny przykład:

package main

import (
    "fmt"
)

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

zagraj na placu zabaw

Tagi

Z polami strukturalnymi mogą być powiązane tagi. Te tagi mogą być odczytywane przez pakiet reflect , aby uzyskać niestandardowe informacje o polu przez dewelopera.

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

W powyższym przykładzie tagi służą do zmiany nazw kluczy używanych przez pakiet encoding/json podczas zestawiania lub rozszyfrowywania JSON.

Chociaż znacznikiem może być dowolna wartość ciągu, najlepszym rozwiązaniem jest użycie par key:"value" oddzielonych spacjami key:"value" :

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

Znaczniki struct używane w pakiecie encoding/xml i encoding/json są używane w całym standardowym lobby.

Robienie kopii struktur.

Strukturę można po prostu skopiować za pomocą przypisania.

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

W powyższym przypadku 't' i „u” są teraz oddzielnymi obiektami (wartościami struktury).

Ponieważ T nie zawiera żadnych typów odniesień (wycinków, mapy, kanałów), ponieważ jego pola t i u powyżej można modyfikować bez wpływu na siebie.

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

Jeśli jednak T zawiera typ odniesienia, na przykład:

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

Następnie prosta kopia według przypisania skopiowałaby również wartość pola typu plasterka do nowego obiektu. Powoduje to, że dwa różne obiekty odnoszą się do tego samego obiektu plasterka.

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

Ponieważ zarówno u i t odnoszą się do tego samego wycinka przez ich pole xs, aktualizacja wartości w wycinku jednego obiektu odzwierciedlałaby zmianę w drugim.

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

Dlatego należy zwrócić szczególną uwagę, aby ta właściwość typu odniesienia nie powodowała niezamierzonego zachowania.

Aby na przykład skopiować powyższe obiekty, można wykonać jawną kopię pola plasterka:

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

Wartość typu struct można zapisać za pomocą literału struct, który określa wartości dla jej pól.

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

Powyższy przykład określa każde pole we właściwej kolejności. Co nie jest przydatne, ponieważ programiści muszą pamiętać dokładne pola w kolejności. Częściej strukturę można zainicjować, wyświetlając niektóre lub wszystkie nazwy pól i odpowiadające im wartości.

anim := gif.GIF{LoopCount: nframes}

Pominięte pola są ustawiane na wartość zerową dla tego typu.

Uwaga: Te dwie formy nie mogą być mieszane w tym samym literale.

Pusta struktura

Struktur jest sekwencją nazwanych elementów, zwanych polami, z których każdy ma nazwę i typ. Pusta struktura nie ma pól, jak ten anonimowy pusty struct:

var s struct{}

Lub jak ten o nazwie pusty typ struktury:

type T struct{}

Ciekawą rzeczą w pustej strukturze jest to, że jej rozmiar wynosi zero (wypróbuj The Go Playground ):

fmt.Println(unsafe.Sizeof(s))

Wyświetla 0 , więc sama pusta struktura nie zajmuje pamięci. więc jest to dobra opcja dla wyjścia z kanału, na przykład (wypróbuj 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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow