defer用法

上一篇小文,我們介紹了defer用法,但是還不是特別清楚,下面我們根據具體的代碼深層次理解defer用法(只是個人理解,如有錯誤之處,還請大佬斧正。)
例子一
咱們直接懟代碼:

package main

import "fmt"

func main() {
    a := 1
    defer fmt.Println("defer a=", a)
    a++
    fmt.Println("a++=", a)
}

結果:

a++=2
defer a=1

可見 defer 關鍵字的函數中如果有參數,此參數定義時就確定了。

例子二

package main

import "fmt"

func main() {
    a := 1
    defer fmt.Println("defer1 a=", a)
    a++
    defer fmt.Println("defer2 a=", a)
    a++
    defer fmt.Println("defer3 a=", a)
    fmt.Println("a=", a)
}

結果:

a=3
defer3 a=3
defer3 a=2
defer3 a=1

可見 在進入函數調用時(比如main函數),在函數的調用棧中的棧頂根據defer的順序依次入棧,當函數執行結束(比如main函數)即將返回(包括正常和異常)時,defer函數按照後進先出的順序依次執行出棧。

例子三

package main

import (
    "log"
    "time"
)

func main() {
    bigSlowOperation()
}
func bigSlowOperation() {
    defer trace("bigSlowOperation")
    time.Sleep(10 * time.Second)
}
func trace(msg string) func() {
    start := time.Now()
    log.Printf("enter %s", msg)
    return func() {
        log.Printf("exit %s (%s)", msg, time.Since(start))
    }
}

結果:

10秒之後輸出
2017/03/15 20:47:09 enter bigSlowOperation

對於這個結果大家可能都知道,先執行time.Sleep(10 * time.Second)
,程序結束執行執行defer trace(“bigSlowOperation”)。
大家看看下面的程序:

package main

import (
    "log"
    "time"
)

func main() {
    bigSlowOperation()
}
func bigSlowOperation() {
    defer trace("bigSlowOperation")()
    time.Sleep(10 * time.Second)
}
func trace(msg string) func() {
    start := time.Now()
    log.Printf("enter %s", msg)
    return func() {
        log.Printf("exit %s (%s)", msg, time.Since(start))
    }
}

細心的同學該發現了,只是再上面那個程序的defer trace(“bigSlowOperation”) 之後加上了(),那麼加上了這個(),程序的執行結果是多少呢?

沒錯結果是:

2017/03/15 20:55:08 enter bigSlowOperation
2017/03/15 20:55:18 exit bigSlowOperation (10.0146469s)

沒錯,程序先執行trace(msg string)函數,延遲10s後defer trace()函數的返回函數 。

那麼可以看到每一次bigSlowOperation被調用,程序都會記錄函數的進入,退出,持續時間。(我們用time.Sleep模擬一個耗時的操作)

例子四
defer語句中的函數會在return語句更新返回值變量後再執行,又因爲在函數中定義的匿名函數可以訪問該函數包括返回值變量在內的所有變量,所以,對匿名函數採用defer機制,可以使其觀察函數的返回值。
看下程序:

package main

import "fmt"

func main() {
    double(4)
}
func double(x int) (result int) {
    defer func() { fmt.Printf("double(%d) = %d\n", x, result) }()
    x++
    return x + x
}

程序運行結果:

double(4) = 10

例子五

對於循環關流的例子如:

for _, filename := range filenames {
     if f, err = os.Open(filename ); err != nil {
         return
     }
     f.Close()
 }

如果我們使用defer關流可以使用defer特性(外函數執行完之後才執行defer 後函數的特性),那麼我們把該程序改寫成這樣

for _, filename := range filenames {
if err := doFile(filename); err != nil {
return err
}
}
func doFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
}

怎麼樣也許你對defer的用法可能有個大概的瞭解了,再加以練習更深入的瞭解defer了。(本人也是新手,如用理解分析不當有誤指出,還請您予以指正。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章