Sök…


Introduktion

Ett defer uttalande skjuter ett funktionssamtal till en lista. Listan över sparade samtal körs när den omgivande funktionen har återgått. Uppskjutning används vanligtvis för att förenkla funktioner som utför olika saneringsåtgärder.

Syntax

  • skjuta upp vissaFunc (args)
  • skjuta upp func () {// kod går hit} ()

Anmärkningar

Uppskjutning fungerar genom att injicera en ny stapelram (den kallade funktionen efter defer nyckelord) i samtalstaket under den nuvarande exekverande funktionen. Detta innebär att uppskjutning garanteras att köra så länge stapeln kommer att avvikas (om ditt program kraschar eller får en SIGKILL , kommer inte uppskjutningen att köras).

Uppskjutna grunder

Ett uppskjutande uttalande i Go är helt enkelt ett funktionssamtal som är markerat för att utföras vid ett senare tillfälle. Uppskjutande uttalande är ett vanligt funktionssamtal förinställt av nyckelordet defer .

defer someFunction()

En uppskjuten funktion exekveras när funktionen som innehåller defer uttalande returneras. Det faktiska samtalet till den uppskjutna funktionen sker när den bifogade funktionen:

  • verkställer ett avkastningsförklaring
  • faller bort från slutet
  • panik

Exempel:

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

Produktion:

First main statement
Last main statement
Function main returned

Om en funktion har flera uppskjutna uttalanden, bildar de en stack. Den sista defer är den första som körs efter att den medföljande funktionen återgår, följt av efterföljande samtal till föregående defer i ordning (nedan exempel återgår genom att orsaka 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)
}

Produktion:

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

goroutine 1 [running]:
....

Observera att uppskjutna funktioner har sina argument utvärderats vid tidpunkten för 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)
}

Produktion:

First main statement
Num 4
Num 2
Num 1

Om en funktion har namngivit returvärden, kan en uppskjuten anonym funktion inom den funktionen komma åt och uppdatera det returnerade värdet även efter att funktionen har returnerats:

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
}

Slutligen används ett defer uttalande vanligtvis operationer som ofta förekommer tillsammans. Till exempel:

  • öppna och stäng en fil
  • ansluta och koppla bort
  • låsa och låsa upp en mutex
  • markera en väntgrupp som gjort ( defer wg.Done() )

Denna användning säkerställer korrekt frisättning av systemresurser oberoende av flöde av körning.

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

Uppskjuten funktionssamtal

Uppskjutna funktionssamtal tjänar ett liknande syfte som saker som finally blockerar i språk som Java: de säkerställer att någon funktion kommer att köras när den yttre funktionen återgår, oavsett om ett fel inträffade eller vilket returrätt som träffades i fall med flera returer. Detta är användbart för att rensa upp resurser som måste stängas som nätverksanslutningar eller filpekare. Den defer nyckelordet indikerar en uppskjuten samtalsfunktion, i likhet med go nyckelordet initierar en ny goroutine. Som en go samtal är funktionsargument utvärderas omedelbart, men till skillnad från en go samtal är uppskjutna funktioner inte exekveras samtidigt.

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

Observera användningen av conn.Close() istället för conn.Close - du passerar inte bara en funktion utan du skjuter upp ett fullfunktionssamtal , inklusive dess argument. Flera funktionssamtal kan skjutas upp i samma yttre funktion, och varje utförs en gång i omvänd ordning. Du kan också skjuta upp stängningar - glöm inte föräldrarna!

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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow