Es ist für mich sehr unklar, in welchem Fall ich einen Wertempfänger verwenden möchte, anstatt immer einen Zeigerempfänger zu verwenden.
So rekapitulieren Sie aus den Dokumenten:
type T struct {
a int
}
func (tv T) Mv(a int) int { return 0 } // value receiver
func (tp *T) Mp(f float32) float32 { return 1 } // pointer receiver
In den Dokumenten heißt es auch: "Für Typen wie Basistypen, Slices und kleine Strukturen ist ein Wertempfänger sehr billig. Wenn die Semantik der Methode keinen Zeiger erfordert, ist ein Wertempfänger effizient und klar."
Der erste Punkt besagt, dass es "sehr billig" ist, aber die Frage ist mehr, ob es billiger ist als der Zeigerempfänger. Also habe ich einen kleinen Benchmark (Code on Gist) erstellt, der mir zeigte, dass der Zeigerempfänger selbst für eine Struktur mit nur einem Zeichenfolgenfeld schneller ist. Dies sind die Ergebnisse:
// Struct one empty string property
BenchmarkChangePointerReceiver 2000000000 0.36 ns/op
BenchmarkChangeItValueReceiver 500000000 3.62 ns/op
// Struct one zero int property
BenchmarkChangePointerReceiver 2000000000 0.36 ns/op
BenchmarkChangeItValueReceiver 2000000000 0.36 ns/op
(Bearbeiten: Bitte beachten Sie, dass der zweite Punkt in neueren Go-Versionen ungültig wurde, siehe Kommentare) .
Der zweite Punkt besagt, dass es "effizient und klar" ist, was eher Geschmackssache ist, nicht wahr? Persönlich bevorzuge ich Konsistenz, indem ich sie überall gleich verwende. Effizienz in welchem Sinne? In Bezug auf die Leistung scheint es, dass Zeiger fast immer effizienter sind. Nur wenige Testläufe mit einer int-Eigenschaft zeigten einen minimalen Vorteil des Value-Empfängers (Bereich von 0,01 bis 0,1 ns / op).
Kann mir jemand einen Fall sagen, in dem ein Wertempfänger eindeutig sinnvoller ist als ein Zeigerempfänger? Oder mache ich im Benchmark etwas falsch, habe ich andere Faktoren übersehen?
Antworten:
Beachten Sie, dass in den häufig gestellten Fragen die Konsistenz erwähnt wird
Wie in diesem Thread erwähnt :
Jetzt:
Der Kommentar zur Codeüberprüfung kann helfen:
Der fett gedruckte Teil befindet sich beispielsweise in
net/http/server.go#Write()
:quelle
The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers
Eigentlich nicht wahr. Sowohl Wertempfänger- als auch Zeigerempfängermethoden können für einen korrekt eingegebenen Zeiger oder Nichtzeiger aufgerufen werden. Unabhängig davon, wie die Methode aufgerufen wird, bezieht sich die Kennung des Empfängers innerhalb des Methodenkörpers auf einen ByInt(5).increment_by_one_ptr()
. Ebenso wird ein Merkmal, das die Methode definiert,increment_by_one_ptr
nicht mit einem Wert vom Typ zufrieden seinInt
.Um zusätzlich zu @VonC eine großartige, informative Antwort hinzuzufügen.
Ich bin überrascht, dass niemand die Wartungskosten wirklich erwähnt hat, sobald das Projekt größer wird, alte Entwickler gehen und neue kommen. Go ist sicher eine junge Sprache.
Im Allgemeinen versuche ich, Zeiger zu vermeiden, wenn ich kann, aber sie haben ihren Platz und ihre Schönheit.
Ich benutze Zeiger, wenn:
Z.B:
Gründe, warum ich Zeiger vermeide:
Meine Faustregel lautet: Schreiben Sie so viele gekapselte Methoden wie möglich, z.
AKTUALISIEREN:
Diese Frage hat mich dazu inspiriert, das Thema genauer zu untersuchen und einen Blog-Beitrag darüber zu schreiben: https://medium.com/gophersland/gopher-vs-object-oriented-golang-4fa62b88c701
quelle