In Go verzögert das Schlüsselwort defer die Ausführung einer Funktion bis zum Abschluss der umgebenden Funktion. Dies ist praktisch zur Freigabe von Ressourcen und zur Bereinigung. Die Verwendung von defer in Kombination mit Methoden, die Zeiger (*T) oder Werte (T) annehmen, hat jedoch nicht offensichtliche Nuancen.
Aus Sicherheitsgründen wurde Go ursprünglich entworfen, um die automatische Aufruf von Bereinigungscode zu garantieren, unabhängig davon, wo die Funktion verlassen wird. Der verwendete Empfänger-Typ (Zeiger oder Wert) bestimmt jedoch, ob das Objekt der aufrufenden Methode kopiert wird oder ob das Originalobjekt geändert wird.
Wenn wir eine Methode mit einem Wert-Empfänger (T) aufrufen, wird der Wert der Struktur in defer kopiert. Wird eine Methode mit einem Zeiger-Empfänger (*T) aufgerufen, arbeitet die Methode mit dem Originalobjekt. Infolgedessen sind Änderungen an den Daten in der defer-Methode durch Wert nicht sichtbar, während sie durch Zeiger in der äußeren Struktur reflektiert werden. Dies führt zu schwer fassbaren Fehlern, insbesondere wenn versucht wird, den Zustand eines Objekts über defer zu ändern.
Bei der Gestaltung von Methoden und der Verwendung von defer sollte immer bewusst der Typ des Empfängers ausgewählt werden. Änderungen, die bis zum Ende der Funktion bestehen bleiben sollen, sollten über Zeiger erfolgen.
Beispielcode:
package main import "fmt" type Counter struct { Value int } func (c Counter) IncValue() { // Methode nach Wert defer func() { c.Value++ // nur die Kopie wird erhöht fmt.Println("[Wert-Empfänger defer] Wert:", c.Value) }() } func (c *Counter) IncPointer() { // Methode nach Zeiger defer func() { c.Value++ // das Original wird erhöht fmt.Println("[Zeiger-Empfänger defer] Wert:", c.Value) }() } func main() { c := Counter{Value: 10} c.IncValue() // Wert bleibt 10 c.IncPointer() // Wert wird 11 fmt.Println("Ursprünglicher Wert nach Aufrufen:", c.Value) }
Wichtige Eigenschaften:
1. Wird das äußere Objekt geändert, wenn in defer eine Methode aufgerufen wird, die einen Wert-Empfänger annimmt?
Nein, das ursprüngliche Objekt wird in defer nicht geändert, da die Methode mit einer Kopie der Struktur arbeitet.
2. Kann man sich auf defer verlassen, um den Zustand einer Struktur durch eine Methode nach Wert garantiert zu ändern?
Nein. Sie sollten Methoden mit Zeigern verwenden, wenn Sie Änderungen am Originalobjekt widerspiegeln möchten.
3. Was passiert, wenn die Felder der Struktur nach der Deklaration von defer geändert werden?
Die Aktionen hängen von der Art der Übergabe des Empfängers ab: Wenn die Methode/Funktion eine Kopie erhält, sind Änderungen nach der Deklaration von defer in der aufgeschobenen Funktion nicht sichtbar.
func (c Counter) Demo() { defer fmt.Println("defer c.Value:", c.Value) // erfasst den aktuellen Wert c.Value = 42 // beeinflusst nicht defer }
Wir haben eine Funktion zum Schließen einer Verbindung geschrieben, die den Zustand über defer mit einer Methode nach Wert aktualisierte. Dabei stellte sich heraus, dass das Schließflag nicht aktualisiert wurde.
Vorteile:
Nachteile:
Wir haben Methoden mit Zeiger-Empfänger zur Bereinigung verwendet, um das Originalobjekt zu ändern.
Vorteile:
Nachteile: