1. 程式人生 > >go-defer語句

go-defer語句

方法 open [] 變量 命名 所表 調用 lose spa

Go語言中的defer語句也非常獨特。
defer語句僅能被放置在函數或方法中。
它由關鍵字defer和一個調用表達式組成。

調用表達式所表示的既不能是對Go語言內建函數的調用
也不能是對Go語言標準庫代碼包unsafe中的那些函數的調用。
實際上,滿足上述條件的調用表達式被稱為表達式語句。
func readFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
return ioutil.ReadAll(file) }
打開指定文件且未發現有錯誤發生之後,緊跟了一條defer語句。 其中攜帶的表達式語句表示的是對被打開文件的關閉操作。 註意,當這條defer語句被執行的時候,其中的這條表達式語句並不會被立即執行。 它的確切的執行時機是在其所屬的函數(這裏是readFile)的執行即將結束的那個時刻。 在readFile函數真正結束執行的前一刻,file.Close()才會被執行。 這也是defer語句被如此命名的原因。

語句defer file.Close()的含義是在打開文件並讀取其內容後及時地關閉它。
該語句可以保證在readFile函數將結果返回給調用方之前,那個文件或目錄一定會被關閉。
這實際上是一種非常便捷和有效的保險措施。

當一個函數中存在多個defer語句時,它們攜帶的表達式語句的執行順序一定是它們的出現順序的倒序。
下面的示例可以很好的證明這一點:
func deferIt() {
    defer func() {
        fmt.Print(1)
    }()
    defer func() {
        fmt.Print(2)
    }()
    defer func() {
        fmt.Print(3)
    }()
    fmt.Print(4)
}
deferIt函數的執行會使標準輸出上打印出4321

defer攜帶的表達式語句代表的是對某個函數或方法的調用。這個調用可能會有參數傳入。
如果代表傳入參數的是一個表達式,那麽在defer語句被執行的時候該表達式就會被求值了。
func deferIt3() {
    f :
= func(i int) int { fmt.Printf("%d ",i) return i * 10 } for i := 1; i < 5; i++ { defer fmt.Printf("%d ", f(i)) } } 它在被執行之後,標準輸出上打印出1 2 3 4 40 30 20 10

defer攜帶的表達式語句代表的是對匿名函數的調用,那麽我們就一定要非常警惕。請看下面的示例:
func deferIt4() {
    for i := 1; i < 5; i++ {
        defer func() {
            fmt.Print(i)
        }()
    }
}     
deferIt4函數在被執行之後標出輸出上會出現5555,而不是4321
原因是defer語句攜帶的表達式語句中的那個匿名函數包含了對外部(確切地說,是該defer語句之外)的變量的使用。
註意,等到這個匿名函數要被執行(且會被執行4次)的時候,包含該defer語句的那條for語句已經執行完畢了。
此時的變量i的值已經變為了5。因此該匿名函數中的打印函數只會打印出5。

正確的用法是:把要使用的外部變量作為參數傳入到匿名函數中。
修正後如下:
func deferIt4() {
    for i := 1; i < 5; i++ {
        defer func(n int) {
            fmt.Print(n)
        }(i)
    }
}

go-defer語句