Sök…
Introduktion
Strukturer är uppsättningar av olika variabler packade ihop. Strukturen i sig är bara ett paket som innehåller variabler och gör dem lättillgängliga.
Till skillnad från i C kan Go's strukturer ha metoder kopplade till dem. Det gör att de också kan implementera gränssnitt. Det gör Gos strukturer som liknar objekt, men de saknar (förmodligen avsiktligt) några viktiga funktioner kända på objektorienterade språk som arv.
Grunddeklaration
En grundstruktur förklaras enligt följande:
type User struct {
FirstName, LastName string
Email string
Age int
}
Varje värde kallas ett fält. Fält skrivs vanligtvis en per rad, med fältets namn före dess typ. Påföljande fält av samma typ kan kombineras som FirstName
och LastName
i exemplet ovan.
Exporterade kontra oexporterade fält (privat vs offentligt)
Strukturfält vars namn börjar med en stor bokstav exporteras. Alla andra namn exporteras inte.
type Account struct {
UserID int // exported
accessToken string // unexported
}
Oporterade fält kan endast nås med kod inom samma paket. Som sådan, om du någonsin kommer åt ett fält från ett annat paket, måste namnet börja med en stor bokstav.
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
}
}
Men inifrån bank
kan du komma åt både användarnamn och accessToken utan problem.
Paketet bank
skulle kunna genomföras så här:
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
}
Sammansättning och inbäddning
Sammansättning ger ett alternativ till arv. En struktur kan innehålla en annan typ efter namn i sin förklaring:
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
I exemplet ovan kommer AuthenticatedRequest
att innehålla fyra offentliga medlemmar: Resource
, Request
, Username
och Password
.
Sammansatta strukturer kan instanseras och användas på samma sätt som vanliga strukturer:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
inbäddning
I föregående exempel är Request
ett inbäddat fält. Sammansättningen kan också uppnås genom inbäddning av en annan typ. Detta är till exempel användbart för att dekorera en struktur med mer funktionalitet. Fortsätt till exempel med resursexemplet, vi vill ha en funktion som formaterar innehållet i resursfältet för att prefixera det med http://
eller https://
. Vi har två alternativ: skapa de nya metoderna på AuthenticatedRequest eller bädda in dem från en annan struktur:
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
}
Och nu kan huvudfunktionen göra följande:
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)
}
Se att AuthenticatedRequest
som har en ResourceFormatter
inbäddad struktur.
Men nackdelen med det är att du inte kan komma åt objekt utanför din komposition. Så ResourceFormatter
kan inte komma åt medlemmar från AuthenticatedRequest
.
metoder
Strukturmetoder liknar funktioner:
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
Den enda skillnaden är tillägget av metodmottagaren. Det kan förklaras antingen som en instans av typen eller en pekare till en instans av typen. Eftersom SetName()
muterar instansen måste mottagaren vara en pekare för att genomföra en permanent ändring av förekomsten.
Till exempel:
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())
}
Anonym struktur
Det är möjligt att skapa en anonym struktur:
data := struct {
Number int
Text string
} {
42,
"Hello world!",
}
Fullständigt exempel:
package main
import (
"fmt"
)
func main() {
data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
fmt.Printf("%+v\n", data)
}
Tags
Strukturfält kan ha taggar associerade med dem. Dessa taggar kan läsas av reflect
för att få anpassad information angiven om ett fält av utvecklaren.
struct Account {
Username string `json:"username"`
DisplayName string `json:"display_name"`
FavoriteColor string `json:"favorite_color,omitempty"`
}
I exemplet ovan används taggarna för att ändra nyckelnamnen som används av encoding/json
paketet när man marsjalerar eller avmarkerar JSON.
Medan taggen kan vara vilket strängvärde som helst, anses det vara bästa praxis att använda mellanseparerad key:"value"
-par:
struct StructName {
FieldName int `package1:"customdata,moredata" package2:"info"`
}
Strukturtaggarna som används med encoding/xml
och encoding/json
paketet används i hela biblioteket.
Skapa strukturkopior.
En struktur kan helt enkelt kopieras med uppdrag.
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"
}
I ovanstående fall är 't'
och 'u' nu separata objekt (strukturvärden).
Eftersom T
inte innehåller några referenstyper (skivor, karta, kanaler) som dess fält, kan t
och u
ovan ändras utan att påverka varandra.
fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1
Om T
innehåller en referenstyp, till exempel:
type T struct {
I int
S string
xs []int // a slice is a reference type
}
Sedan kopierar en enkel kopia efter tilldelning även värdet på skivtypfältet till det nya objektet. Detta skulle resultera i två olika objekt som hänvisar till samma skivaobjekt.
// 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
Eftersom både u och t hänvisar till samma skiva genom deras fält xs som uppdaterar ett värde i segmentet för ett objekt skulle återspegla förändringen i det andra.
// 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]
Därför måste extra försiktighet vidtas för att se till att denna egenskap av referenstyp inte ger oavsiktligt beteende.
För att exempelvis kopiera objekt ovan, kan en explicerad kopia av skivfältet utföras:
// 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]
Strukturbokstäver
Ett värde av en strukturtyp kan skrivas med en strukturell bokstav som anger värden för dess fält.
type Point struct { X, Y int }
p := Point{1, 2}
Exemplet ovan specificerar alla fält i rätt ordning. Vilket är inte användbart eftersom programmerare måste komma ihåg exakta fält i ordning. Oftare kan en struktur initialiseras genom att lista några eller alla fältnamn och deras motsvarande värden.
anim := gif.GIF{LoopCount: nframes}
Utelämnade fält är inställda på nollvärdet för dess typ.
Obs: De två formerna kan inte blandas i samma bokstav.
Tom struktur
En struktur är en sekvens av namngivna element, kallade fält, som alla har ett namn och en typ. Tom struct har inga fält, som den anonyma tomma strukturen:
var s struct{}
Eller som den här namnet tomma strukturen:
type T struct{}
Det intressanta med den tomma strukturen är att storleken är noll (prova Go Playground ):
fmt.Println(unsafe.Sizeof(s))
Detta skriver ut 0
, så den tomma strukturen i sig tar inget minne. så det är ett bra alternativ för att avsluta kanalen, som (prova 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.")
}