image

  • 經常在處理檔案的開關必須要注意到file open就一定要有搭配的 file close.但是如果有很多的case,搭配著很多的return.那是不是就得在每個地方寫上fclose?
  

func FileProcess() error {
    //開啟檔案
    f, err := os.Create("/tmp/dat2")
    check(err)
    
    //在這裡呼叫 defer f.Close(),如果有參數會這時候讀入
    defer f.Close()
    
    if CASE1 {
        Do something        
        //這時候會執行 f.Close()
        return  errors.New("Error Case2")
    } else if CASE2 {
        Do something
        //這時候會執行 f.Close()
        return errors.New("Error Case2")
    } 
    // 就算之後要增加新的case,也不用擔心要補 f.Close()
    
    //這時候會執行 f.Close()
    return nil
}

  • Golang裡面有個很方便的function 叫做defer,他執行的時間點會是在你離開目前的function.
  • 不過這裡需要注意的是有兩個地方:
    • 變數的傳遞,會在呼叫defer的時候傳入.所以他並不是很簡單的直接移到最後呼叫
      • 根據GoDoc Defer: The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes.
    • 呼叫與執行defer採取的是LIFO.
  • 參考這篇文章
  • 結論:
    • defer最好還是使用在fopen與fclose比較不會有問題.
    • 如果有參數要帶,千萬要想清楚當時參數的順序與是否有其他問題會發生.
  

package main

import (
	"errors"
	"fmt"
)

func useAClosure() error {
	var err error
	defer func() {
		fmt.Printf("useAClosure: err has value %v\n", err)
	}()

	err = errors.New("Error 1")
	fmt.Println("Finish func useAClosure")

	return err
}

func deferPrintf() error {
	var err error
    //呼叫的時候會把參數帶過去,所以err是nil
	defer fmt.Printf("deferPrintf: err has value %v\n", err)

	err = errors.New("Error 2")

    //注意這裡是LIFO,所以呼叫會是43210
	for i := 0; i < 5; i++ {
		defer fmt.Printf("%d ", i)
	}
	fmt.Println("Finish func deferPrintf")
	return err
}

func main() {
	useAClosure()
	deferPrintf()
}

/* output
Finish func useAClosure
useAClosure: err has value Error 1
Finish func deferPrintf
4 3 2 1 0 deferPrintf: err has value 
*/
</pre>

Evan

Attitude is everything