转载

Defer, Panic 和 Recover

Defer, Panic 和 Recover 这三个概念是 Golang 中独有的, 也是最基础的, 因此有必要将他们弄懂.

Golang Blog 上有一篇 Defer, Panic, and Recover , 是很好的学习材料.

本文是对这篇文章的笔记.

defer

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 失败了, 函数会直接退出, 而文件没有正常关闭.

这个问题是可以修正的, 例如可以对两个错误判断的 if 里面都加上文件关闭操作.

但是并不是所有的 Bug 都这么显而易见, 这就是 defer 引入语法索要解决的问题.

通过使用 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.被 deferred 的函数的参数在 defer 语句被求值时进行求值:

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

在本例中, defer fmt.Println(i) 中 i 的值取的是 1.

  1. 被 deferred 的函数的执行顺序按照后入先出的顺序:
func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

在上例中, 会先输出 3, 再 2, 再 1, 0.

3.被 deferred 的函数可以读写函数的有名字的返回值:

func c() (i int) {
    defer func() { i++ }()
    return 1
}

在上例中, 这个函数的返回值不是 1, 而是 2.

原文  http://www.judymax.com/archives/1205
正文到此结束
Loading...