Suche…


Einführung

Eine defer Anweisung drückt einen Funktionsaufruf in eine Liste. Die Liste der gespeicherten Anrufe wird ausgeführt, nachdem die umgebende Funktion zurückgegeben wurde. Defer wird häufig verwendet, um Funktionen zu vereinfachen, die verschiedene Bereinigungsaktionen ausführen.

Syntax

  • someFunc (args) verschieben
  • defer func () {// code geht hier hin} ()

Bemerkungen

Defer funktioniert, indem ein neuer Stack-Frame (die nach dem Schlüsselwort defer aufgerufene Funktion) in den Aufruf-Stack unterhalb der aktuell ausgeführten Funktion eingefügt wird. Dies bedeutet, dass die Verzögerung verzögert wird, solange der Stapel abgewickelt wird (wenn Ihr Programm abstürzt oder einen SIGKILL erhält, wird die Verzögerung nicht ausgeführt).

Defer Grundlagen

Eine verzögerte Anweisung in Go ist einfach ein Funktionsaufruf, der zu einem späteren Zeitpunkt ausgeführt wird. Defer-Anweisung ist ein gewöhnlicher Funktionsaufruf, dem das Schlüsselwort defer vorangestellt ist.

defer someFunction()

Eine zurückgestellte Funktion wird ausgeführt, sobald die Funktion, die defer Anweisung enthält, zurückgegeben wird. Der tatsächliche Aufruf der verzögerten Funktion erfolgt, wenn die einschließende Funktion:

  • führt eine return-Anweisung aus
  • fällt vom Ende ab
  • Panik

Beispiel:

func main() {
    fmt.Println("First main statement")
    defer logExit("main") // position of defer statement here does not matter
    fmt.Println("Last main statement")
}

func logExit(name string) {
    fmt.Printf("Function %s returned\n", name)
}

Ausgabe:

First main statement
Last main statement
Function main returned

Wenn eine Funktion mehrere zurückgestellte Anweisungen hat, bilden sie einen Stapel. Der letzte defer ist der erste, der ausgeführt wird, nachdem die einschließende Funktion zurückgegeben wurde, gefolgt von nachfolgenden Aufrufen der vorhergehenden defer in der Reihenfolge (das folgende Beispiel führt zu einer Panik):

func main() {
    defer logNum(1)
    fmt.Println("First main statement")
    defer logNum(2)
    defer logNum(3)
    panic("panic occurred")
    fmt.Println("Last main statement") // not printed
    defer logNum(3) // not deferred since execution flow never reaches this line
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

Ausgabe:

First main statement
Num 3
Num 2
Num 1
panic: panic occurred

goroutine 1 [running]:
....

Beachten Sie, dass die Argumente für verzögerte Funktionen zum Zeitpunkt der Ausführung der defer ausgewertet werden:

func main() {
    i := 1
    defer logNum(i) // deferred function call: logNum(1)
    fmt.Println("First main statement")
    i++
    defer logNum(i) // deferred function call: logNum(2)
    defer logNum(i*i) // deferred function call: logNum(4)
    return // explicit return
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

Ausgabe:

First main statement
Num 4
Num 2
Num 1

Wenn eine Funktion Rückgabewerte benannt hat, kann eine zurückgestellte anonyme Funktion innerhalb dieser Funktion auf den zurückgegebenen Wert zugreifen und ihn aktualisieren, auch nachdem die Funktion zurückgegeben wurde:

func main() {
    fmt.Println(plusOne(1)) // 2
    return
}

func plusOne(i int) (result int) { // overkill! only for demonstration
    defer func() {result += 1}() // anonymous function must be called by adding ()

    // i is returned as result, which is updated by deferred function above
    // after execution of below return
    return i
}

Eine defer ist in der Regel eine Operation, die häufig zusammen vorkommt. Zum Beispiel:

  • öffnen und schließen Sie eine Datei
  • verbinden und trennen
  • sperren und entsperren Sie einen Mutex
  • Eine Wartegruppe als erledigt markieren ( defer wg.Done() )

Diese Verwendung gewährleistet die ordnungsgemäße Freigabe von Systemressourcen unabhängig vom Ausführungsfluss.

resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed

Aufgeschobene Funktionsaufrufe

Latente Funktionsaufrufe dienen einem ähnlichen Zweck , um Dinge wie finally Blöcke in Sprachen wie Java: Sie sorgen dafür , dass eine bestimmte Funktion ausgeführt wird, wenn die äußeren Funktion zurückkehrt, unabhängig davon , ob ein Fehler aufgetreten ist oder welche Anweisung Rückkehr wurde in Fällen mit mehreren Rückkehr getroffen. Dies ist nützlich, um Ressourcen zu bereinigen, die wie Netzwerkverbindungen oder Dateizeiger geschlossen werden müssen. Das defer Schlüsselwort gibt einen latenten Funktionsaufruf, ähnlich wie bei dem go Begriff neu goroutine initiieren. Funktionsargumente werden wie ein go Aufruf sofort ausgewertet, aber im Gegensatz zu einem go Aufruf werden aufgeschobene Funktionen nicht gleichzeitig ausgeführt.

func MyFunc() {
    conn := GetConnection()    // Some kind of connection that must be closed.
    defer conn.Close()        // Will be executed when MyFunc returns, regardless of how.
    // Do some things...
    if someCondition {
        return                // conn.Close() will be called
    }
    // Do more things
}// Implicit return - conn.Close() will still be called

Beachten Sie die Verwendung von conn.Close() statt conn.Close - Sie sind nicht nur auf der Durch in einer Funktion, werden Sie eine volle Funktionsaufruf aufzuschieben, einschließlich seiner Argumente. Mehrere Funktionsaufrufe können in derselben äußeren Funktion zurückgestellt werden und werden jeweils einmal in umgekehrter Reihenfolge ausgeführt. Sie können Schließungen auch verschieben - vergessen Sie nicht die Parens!

defer func(){
    // Do some cleanup
}()


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow