Szukaj…


Wprowadzenie

Instrukcja defer wypycha wywołanie funkcji na listę. Lista zapisanych wywołań jest wykonywana po powrocie otaczającej funkcji. Odroczenie jest powszechnie stosowane w celu uproszczenia funkcji wykonujących różne czynności czyszczenia.

Składnia

  • odłóż someFunc (args)
  • defer func () {// kod idzie tutaj} ()

Uwagi

Odroczenie działa poprzez wstrzyknięcie nowej ramki stosu (funkcja wywoływana po słowie kluczowym defer ) do stosu wywołań poniżej aktualnie wykonywanej funkcji. Oznacza to, że odroczenie jest gwarantowane tak długo, jak stos będzie rozwijany (jeśli twój program ulegnie awarii lub otrzyma SIGKILL , odroczenie nie zostanie wykonane).

Odłóż podstawy

Instrukcja odroczenia w Go jest po prostu wywołaniem funkcji oznaczonym do wykonania w późniejszym czasie. Instrukcja defer jest zwykłym wywołaniem funkcji poprzedzonym słowem kluczowym defer .

defer someFunction()

Funkcja odroczona jest wykonywana po zwróceniu funkcji zawierającej instrukcję defer . Rzeczywiste wywołanie funkcji odroczonej występuje, gdy funkcja zamykająca:

  • wykonuje instrukcję return
  • spada z końca
  • panika

Przykład:

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)
}

Wynik:

First main statement
Last main statement
Function main returned

Jeśli funkcja ma wiele odroczonych instrukcji, tworzą one stos. Ostatnie defer jest pierwszym, które zostanie wykonane po zwróceniu funkcji zamykającej, a następnie kolejne wywołania w kolejności defer w kolejności (poniżej przykład zwraca, powodując 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)
}

Wynik:

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

goroutine 1 [running]:
....

Zauważ, że funkcje odroczone są poddawane ocenie ich argumentów w czasie wykonywania defer :

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)
}

Wynik:

First main statement
Num 4
Num 2
Num 1

Jeśli funkcja nazywa wartości zwrotne, odroczona funkcja anonimowa w ramach tej funkcji może uzyskać dostęp do zaktualizowanej wartości i zaktualizować ją nawet po jej zwróceniu:

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
}

Wreszcie, instrukcja defer to zwykle używane operacje, które często występują razem. Na przykład:

  • otwórz i zamknij plik
  • połączyć i rozłączyć
  • zablokuj i odblokuj muteks
  • oznacz grupę oczekującą jako wykonaną ( defer wg.Done() )

Takie zastosowanie zapewnia prawidłowe zwolnienie zasobów systemowych niezależnie od przepływu wykonania.

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

Odroczone wywołania funkcji

Odroczone wywołania funkcji służą do podobnego celu, jak na przykład finally bloki w językach takich jak Java: zapewniają, że niektóre funkcje zostaną wykonane, gdy funkcja zewnętrzna powróci, niezależnie od tego, czy wystąpił błąd lub która instrukcja return została trafiona w przypadkach z wieloma zwrotami. Jest to przydatne do czyszczenia zasobów, które należy zamknąć, takich jak połączenia sieciowe lub wskaźniki plików. defer kluczowe defer wskazuje odroczenie wywołania funkcji, podobnie jak słowo kluczowe go inicjujące nowy goroutine. Podobnie jak wywołanie go , argumenty funkcji są analizowane natychmiast, ale w przeciwieństwie do wywołania go , odroczone funkcje nie są wykonywane jednocześnie.

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

Zwróć uwagę na użycie conn.Close() zamiast conn.Close - nie tylko przekazujesz funkcję, ale odraczasz pełne wywołanie funkcji, w tym jej argumenty. Wiele wywołań funkcji można odroczyć w tej samej funkcji zewnętrznej, a każde z nich zostanie wykonane raz w odwrotnej kolejności. Możesz także odkładać zamknięcia - po prostu nie zapomnij o parens!

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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow