Golang Defer

作者: Wenchao | 来源:发表于2019-03-18 22:39 被阅读0次

    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
      }
      

    相关文章

      网友评论

        本文标题:Golang Defer

        本文链接:https://www.haomeiwen.com/subject/rdbimqtx.html