關(guān)鍵字 defer 允許我們推遲到函數(shù)返回之前(或任意位置執(zhí)行 return
語(yǔ)句之后)一刻才執(zhí)行某個(gè)語(yǔ)句或函數(shù)(為什么要在返回之后才執(zhí)行這些語(yǔ)句?因?yàn)?return
語(yǔ)句同樣可以包含一些操作,而不是單純地返回某個(gè)值)。
關(guān)鍵字 defer 的用法類(lèi)似于面向?qū)ο缶幊陶Z(yǔ)言 Java 和 C# 的 finally
語(yǔ)句塊,它一般用于釋放某些已分配的資源。
示例 6.8 defer.go:
package main
import "fmt"
func main() {
function1()
}
func function1() {
fmt.Printf("In function1 at the top\n")
defer function2()
fmt.Printf("In function1 at the bottom!\n")
}
func function2() {
fmt.Printf("Function2: Deferred until the end of the calling function!")
}
輸出:
In Function1 at the top
In Function1 at the bottom!
Function2: Deferred until the end of the calling function!
請(qǐng)將 defer 關(guān)鍵字去掉并對(duì)比輸出結(jié)果。
使用 defer 的語(yǔ)句同樣可以接受參數(shù),下面這個(gè)例子就會(huì)在執(zhí)行 defer 語(yǔ)句時(shí)打印 0
:
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
當(dāng)有多個(gè) defer 行為被注冊(cè)時(shí),它們會(huì)以逆序執(zhí)行(類(lèi)似棧,即后進(jìn)先出):
func f() {
for i := 0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
}
上面的代碼將會(huì)輸出:4 3 2 1 0
。
關(guān)鍵字 defer 允許我們進(jìn)行一些函數(shù)執(zhí)行完成后的收尾工作,例如:
// open a file
defer file.Close()
mu.Lock()
defer mu.Unlock()
printHeader()
defer printFooter()
// open a database connection
defer disconnectFromDB()
合理使用 defer 語(yǔ)句能夠使得代碼更加簡(jiǎn)潔。
以下代碼模擬了上面描述的第 4 種情況:
package main
import "fmt"
func main() {
doDBOperations()
}
func connectToDB() {
fmt.Println("ok, connected to db")
}
func disconnectFromDB() {
fmt.Println("ok, disconnected from db")
}
func doDBOperations() {
connectToDB()
fmt.Println("Defering the database disconnect.")
defer disconnectFromDB() //function called here with defer
fmt.Println("Doing some DB operations ...")
fmt.Println("Oops! some crash or network error ...")
fmt.Println("Returning from function here!")
return //terminate the program
// deferred function executed here just before actually returning, even if
// there is a return or abnormal termination before
}
輸出:
ok, connected to db
Defering the database disconnect.
Doing some DB operations ...
Oops! some crash or network error ...
Returning from function here!
ok, disconnected from db
使用 defer 語(yǔ)句實(shí)現(xiàn)代碼追蹤
一個(gè)基礎(chǔ)但十分實(shí)用的實(shí)現(xiàn)代碼執(zhí)行追蹤的方案就是在進(jìn)入和離開(kāi)某個(gè)函數(shù)打印相關(guān)的消息,即可以提煉為下面兩個(gè)函數(shù):
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
以下代碼展示了何時(shí)調(diào)用這兩個(gè)函數(shù):
示例 6.10 defer_tracing.go:
package main
import "fmt"
func trace(s string) { fmt.Println("entering:", s) }
func untrace(s string) { fmt.Println("leaving:", s) }
func a() {
trace("a")
defer untrace("a")
fmt.Println("in a")
}
func b() {
trace("b")
defer untrace("b")
fmt.Println("in b")
a()
}
func main() {
b()
}
輸出:
entering: b
in b
entering: a
in a
leaving: a
leaving: b
上面的代碼還可以修改為更加簡(jiǎn)便的版本(示例 6.11 defer_tracing2.go):
package main
import "fmt"
func trace(s string) string {
fmt.Println("entering:", s)
return s
}
func un(s string) {
fmt.Println("leaving:", s)
}
func a() {
defer un(trace("a"))
fmt.Println("in a")
}
func b() {
defer un(trace("b"))
fmt.Println("in b")
a()
}
func main() {
b()
}
使用 defer 語(yǔ)句來(lái)記錄函數(shù)的參數(shù)與返回值
下面的代碼展示了另一種在調(diào)試時(shí)使用 defer 語(yǔ)句的手法(示例 6.12 defer_logvalues.go):
package main
import (
"io"
"log"
)
func func1(s string) (n int, err error) {
defer func() {
log.Printf("func1(%q) = %d, %v", s, n, err)
}()
return 7, io.EOF
}
func main() {
func1("Go")
}
輸出:
Output: 2011/10/04 10:46:11 func1("Go") = 7, EOF