Go語言中的defer關鍵字

來源:互聯網
上載者:User


官方文檔中關於defer語句的解釋:

defer語句順延強制一個函數,該函數被延遲到當包含它的程式返回時(包含它的函數 執行了return語句/運行到函數結尾自動返回/對應的goroutine panic)執行。

每次defer語句執行時,defer修飾的函數的傳回值和參數取值會照常進行計算和儲存,但是該函數不會執行。等到上一級函數返回前,會按照defer的聲明順序倒序執行全部defer的函數。defer的函數的任何傳回值都會被丟棄。

如果一個defer的函數值為nil,則這個defer的函數會在函數執行時panic(異常),而不會在defer語句執行時panic,例如:

func test() {

    f := func(){ fmt.Println("Test") }

    defer f()

    f = nil    // 這裡將函數的“值”設為nil,此時再嘗試執行這個函數的時候

    defer f()

    defer fmt.Println("Test1")

}

 

func main() {

    test()

}

執行結果:

Test1

Test

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xc0000005 code=0x0 addr=0x0 pc=0x451668]

 

goroutine 1 [running]:

panic(0x49aaa0, 0xc042004090)

        D:/Program Files/Go/src/runtime/panic.go:500 +0x1af

main.test()

        D:/Programming/GoWork/defer.go:28 +0xfc

main.main()

        D:/Programming/GoWork/defer.go:31 +0x1b

可以看到,第一個Test1正常輸出了,說明設定f = nil後,defer f()並未直接panic,而是在test()返回之前,倒序執行到第二次f()的時候,拋出異常。同時Test也輸出出來了,說明defer的方法在會在panic之前執行,不會因為panic了而停止執行,對比測試:

func test() {

    f := func(){ fmt.Println("Test") }

    f()

    f = nil

    f()

    fmt.Println("Test1")

}

 

func main() {

    test()

}

執行結果:

Test

panic: runtime error: invalid memory address or nil pointer dereference

[signal 0xc0000005 code=0x0 addr=0x0 pc=0x40107a]

 

goroutine 1 [running]:

panic(0x49aa20, 0xc042004090)

        D:/Program Files/Go/src/runtime/panic.go:500 +0x1af

main.test()

        D:/Programming/GoWork/defer.go:26 +0x3a

main.main()

        D:/Programming/GoWork/defer.go:31 +0x1b

exit status 2

可以看到Test1並未輸出,原因是在執行fmt.Println("Test1")之前test()已經panic了。

 

插播一條panic的處理方法:

通過內建方法recover()可以捕獲panic,recover()方法有一個傳回值,如果recover()捕獲到panic,則返回panic對象,否則返回nil。下面是官方文檔中的一個recover()的例子,封裝了一個方法來捕獲panic(類似於try…catch…):

func test() {

    f := func(){ fmt.Println("Test") }

    f()

    f = nil

    f()

    fmt.Println("Test1")

}

 

func safeFunc(target func()) {

    defer func() {

        if x := recover(); x != nil {

            fmt.Println("Runtime panic: ", x)

        }

    } ()

    target()

}

 

func main() {

    safeFunc(test)

}

執行結果:

Test

Runtime panic:  runtime error: invalid memory address or nil pointer dereference

可以使用panic()函數人工拋出一個panic:

panic("Error Message")

 

 

回到defer的用法,下面舉了幾個例子來說明defer的用法:

樣本1:

func main() {

    for i := 0; i < 3; i++ {

        defer fmt.Println(i)

    }

}

輸出:2 1 0

上面的代碼等價於:

func main() {

    defer fmt.Println(0)    // 每次defer語句執行時,defer修飾的函數的傳回值和參數取值會照常進行計算和儲存

    defer fmt.Println(1)    // 同理

    defer fmt.Println(2)    // 同理

}

即:

func main() {

    fmt.Println(2)

    fmt.Println(1)

    fmt.Println(0)

}

 

樣本2:

func main() {

    for i := 0; i < 3; i++ {

        defer func() { fmt.Println(i) } ()

    }

}

輸出:

3 3 3

上面的代碼等價於:

func main() {

    i := 0

    defer func() { fmt.Println(i) } ()    // defer修飾的函數是閉包函數,而非裡面的fmt語句,故裡面的參數不變

    i++    // i = 1

    defer func() { fmt.Println(i) } ()

    i++    // i = 2

    defer func() { fmt.Println(i) } ()

    i++    // i = 3

}

即:

func main() {

    i := 0

    i++

    i++

    i++

    func() { fmt.Println(i) } ()

    func() { fmt.Println(i) } ()

    func() { fmt.Println(i) } ()

}

 

樣本3:

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.