Warum ist nicht meine Stringer interface-Methode immer aufgerufen wird? Bei der Verwendung von fmt.Println
Denke, ich habe den folgenden code:
package main
import "fmt"
type Car struct{
year int
make string
}
func (c *Car)String() string{
return fmt.Sprintf("{make:%s, year:%d}", c.make, c.year)
}
func main() {
myCar := Car{year:1996, make:"Toyota"}
fmt.Println(myCar)
}
Wenn ich anrufen fmt.Println(myCar) und das Objekt in Frage ist ein Zeiger, my String () - Methode aufgerufen wird, richtig. Wenn jedoch das Objekt ist ein Wert, meine Ausgabe ist formatiert mit dem Standard-Formatierung eingebaut Gehen und meinen code zum formatieren der Sprach-Objekt nicht aufgerufen.
Das interessante an der Sache ist in jedem Fall, wenn ich rufe myCar.String() manuell funktioniert es einwandfrei, ob mein Objekt ist entweder ein Zeiger oder Wert.
Wie kann ich mein Objekt formatiert, wie ich will, egal ob das Objekt Wert-oder pointer-basiert, wenn verwendet mit Println?
Ich will nicht verwenden ein value-Methode für String-denn dann bedeutet dass, dass jedes mal, wenn es aufgerufen wird, das Objekt wird kopiert, die Nähte nicht zumutbar. Und ich will nicht immer manuell aufgerufen .String() entweder, weil ich versuche, lassen die duck-typing-system tun, es ist Arbeit.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Beim Aufruf
fmt.Println
,myCar
implizit in einen Wert vom Typinterface{}
wie Sie sehen können aus die Funktion Unterschrift. Den code aus derfmt
- Paket, dann wird ein Typ-Schalter, um herauszufinden, wie drucken mit diesem Wert, suchen so etwas wie dieses:Jedoch die
fmt.Stringer
Fall nicht, weilCar
nicht umsetzenString
(wie es definiert ist, auf*Car
). AufrufString
manuell funktioniert, weil der compiler sieht, dassString
braucht eine*Car
und damit automatisch konvertiertmyCar.String()
zu(&myCar).String()
. Für alles, was in Bezug auf Schnittstellen, müssen Sie es manuell tun. So haben Sie entweder zu implementierenString
aufCar
oder immer übergeben Sie einen Zeiger auffmt.Println
:Daher, für Ihre
String
Methode zu arbeiten, wenn aufgerufen, auf beiden Zeiger und Werte, ein Wert-receiver. Zum Beispiel,Ausgabe:
interface{}
enthält die Typ-Informationen und einen Zeiger auf die struct. Die Struktur selbst wird nicht kopiert.Definieren fmt.Stringer auf einen Zeiger-Empfänger:
Spielplatz
Ausgabe:
Dann immer übergeben Sie einen Zeiger auf Instanzen von Auto zu fmt.Println. Diese Weise einem potenziell teuer Wert kopieren vermieden, unter Ihrer Kontrolle.
Der OP fragte weiter:
Folgenden experiment ist der Beweis, dass die Antwort ist "ja" (wenn ein Wert Empfänger verwendet wird). Beachten Sie, dass die
String()
- Methode erhöht die Jahr in diesem experiment, und prüfen Sie, wie dieses wirkt sich auf die gedruckte Ausgabe.Mit einem Wert Empfänger
(c Car)
, die folgenden gedruckten Ausgabe zeigt, dass Gehen macht Wert Kopien derCar
struct, da die Jahres-Schritten wird nicht reflektiert, die in nachfolgenden aufrufen zuPrintln
:Ändern der Empfänger einen Zeiger
(c *Car)
aber verändert sonst nichts, die gedruckte Ausgabe wird:Selbst wenn Sie einen Zeiger als argument in einem Aufruf
Println
, d.h.fmt.Println(&myCar)
Gehen macht immer noch einen Wert kopieren derCar
struct , wenn ein Wert Empfänger verwendet wird. Die OP vermeiden will, Wert Kopien gemacht, und mein Fazit ist, dass nur Zeiger Empfänger die Erfüllung dieser Anforderung.Im Allgemeinen ist es am besten zu vermeiden, zuweisen von Werten an Variablen, die über die statischen Initialisierungen, d.h.
Dies ist, weil kann es schaffen, genau die Beschwerde, die Sie reden, wenn Sie vergessen, pass
foo
als Zeiger über&foo
oder Sie sich entscheiden, Wert-Empfängern, die Sie am Ende macht eine Menge Klone Ihrer Werte.Stattdessen versuchen, zuweisen von Zeigern auf statischen Initialisierungen standardmäßig, d.h.
Diese Weise
f
wird immer ein Zeiger und die einzige Zeit erhalten Sie einen Wert kopieren, wenn Sie explizit Wert-Receiver.(Natürlich gibt es Zeiten, wenn Sie wollen, speichern Sie den Wert aus einem static-Initialisierer, aber diese sollten Grenzfälle)