Golang中的延迟代码


GO语言中,defer代码块会在函数调用链表中增加一个函数调用,这个函数调用是发生在return 之后的,通常用来释放函数的内部变量。

假设有一个函数,打开文件并对文件进行若干读写,在这种函数中,经常会有提前返回的情况。如果这样的话,就需要关闭正在工作的文件描述符。可能你会写如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 不用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后面,可以使函数更加可读和健壮。

1
2
3
4
5
6
7
8
9
10
11
12
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
}

我们再来看一个延迟函数的例子:

1
2
3
for i := 0 ; i < 5 ; i++ {
defer fmt.Printf("%d ", i)
}

延迟的函数是按照后进先出(LIFO)的顺序执行的,所以上面for循环的例子会打印:4 3 2 1 0。

利用defer也可以修改返回值,假设正在使用命名结果参数和函数符号,例如下面这个函数:

1
2
3
defer func() {
/* ... */
}() // <- () 在这里是必须的

或者这个例子,我们更容易理解为什么,以及在哪需要用括号。

1
2
3
defer func(x int) {
/* ... */
}(5) // 为输入参数 x 赋值 5

在这个defer匿名函数中,可以访问任何命名返回参数:

1
2
3
4
5
6
7
func f() (ret int) { 
// ret 初始化为零
defer func() {
ret++ // ret 增加为 1
}()
return 0 // 返回的是 1 而不是 0!
}

文章作者: RickDamon
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 RickDamon !
  目录