2016-09-28 13 views
7

Istnieje prosty struct tak:Golang struct inicjalizacji

type Event struct { 
    Id   int 
    Name  string 
} 

Jaka jest różnica między tymi dwiema metodami inicjalizacji?

e1 := Event{Id: 1, Name: "event 1"} 
e2 := &Event{Id: 2, Name: "event 2"} 

Dlaczego miałbym używać jednej z tych metod inicjalizacji?

Dzięki

+3

Tego typu konstrukcje są dość podstawowe rzeczy dla Go i wytłumaczył dobrze w Tour of Go (https://tour.golang.org/). A może jeszcze raz przejdziesz przez trasę? – Volker

+1

Jest subtelnie, co nie jest jasne w trakcie trasy. Istnieje różnica w uzyskiwaniu wskaźnika do samej instancji podczas inicjowania struktury. Kiedy i dlaczego miałbym używać VS w jedną stronę. –

Odpowiedz

10

Pierwsza metoda

e1 := Event{Id: 1, Name: "event 1"} 

inicjalizuje się zmienną e1 jako wartość z rodzaju Event. Drugi

e2 := &Event{Id: 1, Name: "event1"} 

jest inicjowanie e2 jako wskaźnik do wartości typu Event Jak podano w komentarzach, zestaw metod zdefiniowany w wartości danego typu są podzbiorem zbioru metod zdefiniowanej na wskaźniku do wartości tego typu. Oznacza to, że jeśli masz metoda

func (e Event) GetName() string { 
    return e.Name 
} 

następnie zarówno e1 i e2 może wywołać tę metodę, ale gdybyś miał inną metodę, powiedzieć:

func (e *Event) ChangeName(s string) { 
    e.Name = s 
} 

Następnie e1 nie jest w stanie korzystać z ChangeName metoda, podczas gdy e2 jest.

To (e1 nie jest w stanie wykorzystać metodę ChangeName, natomiast e2 jest) nie jest (choć może to być w czasie pisania tej pomocy), dzięki @DannyChen za sprowadzenie tego do góry i @GilbertNwaiwu do testowania i zamieszczania w komentarzach poniżej.

(Aby rozwiązać striked z sekcji powyżej:. Zbiór metod określonych w danym typie struct składać z metod określonych dla danego rodzaju i wskaźniki do rodzaju

Zamiast iść teraz automatycznie dereferences argument do metoda, więc jeśli metoda otrzyma wskaźnik, Go wywołuje metodę na wskaźniku do tej struktury, a jeśli metoda otrzyma wartość, Go wywoła metodę na wartość wskazaną przez tę strukturę. W tym momencie moja próba aktualizacja tej odpowiedzi może zabraknąć czegoś ważnego w semantykach, więc jeśli ktoś chciałby to poprawić lub wyjaśnić, nie krępuj się, aby dodać komentarz wskazujący na bardziej wyczerpującą odpowiedź. Oto trochę z placu zabaw ilustrującego ten problem: https://play.golang.org/p/JcD0izXZGz.

W pewnym stopniu ta zmiana sposobu, w jaki wskaźniki i wartości działają jako argumenty dla metod zdefiniowanych dla funkcji, wpływa na niektóre obszary dyskursu poniżej, ale pozostawiam resztę nieedytowaną, chyba że ktoś zachęci mnie do jej aktualizacji, ponieważ wydaje się, że jest bardziej lub mniej poprawne w kontekście ogólnej semantyki języków, które przechodzą przez wartość vs. wskaźnik.)

Odnośnie różnicy między wskaźnikami i wartościami, ten przykład jest przykładowy, ponieważ wskaźniki są zwykle używane w Go, aby umożliwić mutację wartości wskazywane przez zmienną (ale istnieje wiele innych powodów, dla których można również użyć wskaźników! Chociaż w typowym użyciu jest to zwykle solidne założenie). Tak więc, jeśli zdefiniowano ChangeName zamiast jak:

func (e Event) ChangeName(s string) { 
    e.Name = s 
} 

Funkcja ta nie byłaby bardzo przydatna, gdy zwrócił się do odbiornika wartości, jako wartości (nie kursory) nie będzie zachować zmiany, które zostały wprowadzone do nich, jeśli jesteś przeszedł do funkcji. Ma to związek z powierzchni konstrukcji językowych wokół jak zmienne są przypisane i przeszły: What's the difference between passing by reference vs. passing by value?

Można to zobaczyć na tym przykładzie w Go Playground: https://play.golang.org/p/j7yxvu3Fe6

+0

Świetne wyjaśnienie, dziękuję –

+0

"Wtedy e1 nie może użyć metody ChangeName, podczas gdy e2 jest." czy ten wniosek jest nadal poprawny? –

+0

@DannyChen: po prostu przetestowane. Niepoprawne. Obaj mogliby użyć metody –

2

Rodzaj e1 jest Event rodzaj e2 jest *Event. Inicjalizacja jest w rzeczywistości taka sama (przy użyciu złożonej składni literalnej, nie jest też pewne, czy ten żargon to Go lub C#, czy też oba?), Ale przy użyciu adresu operatora zwraca on wskaźnik do tego obiektu, a nie do instancji samo.

+0

e1.SomeMethod() vs * e2.SomeMethod jest w zasadzie taki sam, prawda? Go będzie robić (* e1) .SomeMethod(), jeśli się nie mylę. –

+0

@sheldon_cooper nie dokładnie, są to różne typy. Gdybym miał metodę taką jak 'func (e * Event) Test()' i próbowałem zrobić 'e1.Test()' otrzymywałbym błąd kompilatora. Mógłbym zrobić "ep: = i e1", a następnie zrobić "ep.Test()", albo mógłbyś to zrobić inline lub cokolwiek, ale musiałbyś pobrać wskaźnik, aby wywołać metodę zdefiniowaną za pomocą wskaźnika jako odbiornika. Zasadniczo, gdy dzwonisz metodami, idź, nie masuj ani nie przymuszaj tego typu. Musi być zgodny z definicją metody. Można jednak łatwo przekonwertować wartość na wskaźnik lub wskaźnik na wartość. – evanmcdonnal

+0

Tak, rozumiem różnicę między referencjami a typami odbiorników. Miałem to na uwadze w specyfikacji "Wywołanie metody xm() jest poprawne, jeśli zbiór metod (typ) x zawiera m, a listę argumentów można przypisać do listy parametrów m. Jeśli x jest adresowalne, a metoda & x's zestaw zawiera m, xm() jest skrótem dla (& x) .m(): "URL do specyfikacji to: https: //golang.org/ref/spec#Calls. Dziękuję za wyjaśnienie. Będę eksperymentować z tym. –