Golang

Golang Defer

2019-03-18  本文已影响0人  Wenchao

Defer

Go的控制流有一些常用的机制:if, for, switch, goto. 它也拥有可以在独立的goroutine中运行代码的go语句。接下来会介绍通常比较少用到的类型:defer, panic, recover.

defer语句将函数调用压入一个列表。当包围defer的函数返回的时候,列表中缓存的调用开始执行。Defer语句是用来简化各种各样的清除操作的

例如,当需要打开两个文件,将其中一个文件的内容拷贝到另外一个文件的时候:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    
    written, err = io.Copy(dst, src)
    dst.Close()
    src.Close()
    return
}

上面的代码可以工作,但是会有一个Bug。如果调用os.Create失败,这个函数就会立即返回却没有关闭源文件。当然,可以在出现error的地方加上src.Close方法,但是如果这个函数非常复杂,这个问题就会很难被注意到。当引入defer语句,这个问题就会很容易被解决:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()
    
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()
    
    return io.Copy(dst, src)
}

Defer 语句可以确保我们打开文件之后会关闭这些文件,不管函数中有多少个return语句。

Defer 语句的行为非常直接,可以预测。有三个简单的规则:

  1. defer语句指定的函数中的变量是当执行到defer语句的时候就确定了的;
    在下面的例子中,表达式“i”的值当Println被defer的时候就确定了的,所以下面的输出是0,而不是1

    func a() {
        i := 0
        defer fmt.Println(i)
        i++
        return
    }
    
  2. 多个defer 语句执行的顺序是后进先出的。
    下面的函数打印“3210”

    func b() {
        for i := 0; i < 4; i++ {
            defer fmt.Print(i)
        }
    }
    
  3. 被Defer的函数可以读取并且修改外层函数的被命名的返回值
    在下面的例子中,当外层函数返回的时候,defer函数执行,并且将返回值加1,所以这个函数返回的是2.

    func c() (i int) {
        defer func() {i++} ()
        return 1
    }
    
上一篇下一篇

猜你喜欢

热点阅读