GO语言中,defer代码块会在函数调用链表中增加一个函数调用,这个函数调用是发生在return 之后的,通常用来释放函数的内部变量。
假设有一个函数,打开文件并对文件进行若干读写,在这种函数中,经常会有提前返回的情况。如果这样的话,就需要关闭正在工作的文件描述符。可能你会写如下的代码:
// 不用defer
func ReadWrite() bool {
file.Open("file")
// 做一些工作
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
在这个函数中出现了很多的重复代码,而defer语句是可以解决这些问题的,在defer后指定的函数会在函数退出前进行调用。
我们可以通过defer语句,将Close对应的内容放置在Open后面,可以使函数更加可读和健壮。
func ReadWrite() bool {
file.Open("file")
defer file.Close() // file.Close() 被添加到了 defer 列表
// 做一些工作
if failureX {
return false // Close() 现在自动调用
}
if failureY {
return false // 这里也是
}
return true // And here
}
我们再来看一个延迟函数的例子:
for i := 0 ; i < 5 ; i++ {
defer fmt.Printf("%d ", i)
}
延迟的函数是按照后进先出(LIFO)的顺序执行的,所以上面for循环的例子会打印:4 3 2 1 0.利用defer也可以修改返回值,假设正在使用命名结果参数和函数符号,例如下面这个函数:
defer func() {
/* ... */
}() // ← () 在这里是必须的
或者这个例子,我们更容易理解为什么,以及在哪需要用括号。
defer func(x int) {
/* ... */
}(5) // 为输入参数 x 赋值 5
在这个defer匿名函数中,可以访问任何命名返回参数:
func f() (ret int) { ← ret 初始化为零
defer func() {
ret++ // ret 增加为 1
}()
return 0 // 返回的是 1 而不是 0!
}