golang是一门简洁、高效、并发友好的编程语言,它提供了许多独特的特性,让程序员可以更容易地编写优雅和健壮的代码。其中一个特性就是defer语句,它可以让我们在函数返回之前执行一些清理或收尾的操作,比如关闭文件、释放资源、解锁互斥量等。defer语句的语法很简单,就是在要延迟执行的函数或方法前加上关键字defer,如下所示:
func main() {
f, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close() // 延迟关闭文件
}
在这个例子中,我们打开了一个文件,并在函数返回之前延迟关闭它,这样可以确保不会因为忘记关闭文件而造成资源泄露。
一、多个defer的执行顺序
defer语句的执行顺序是按照后进先出(LIFO)的原则,也就是说,最后一个defer语句会最先执行,第一个defer语句会最后执行。例如:
func main() {
defer fmt.Println("A")
defer fmt.Println("B")
defer fmt.Println("C")
}
这个程序的输出是:
C
B
A
二、参数赋值的时间
参数的赋值对于有的同学来说一只都是一个难题,那defer是什么时候确定参数的值的呢?我们先来比较下面两个语句输出的差异:
func main() {
x := 1
defer fmt.Println(x)
x = 2
}
这个程序的输出是:
1
x := 1
defer func() {
fmt.Println(x)
}()
x = 2
这个程序的输出是:
2
如果熟悉go异步编程的小伙伴应该不会觉得惊讶。原因defer 调用的函数参数的值在 defer 定义时就确定了, 而 defer 函数内部所使用的变量的值需要在这个函数运行时才确定。所以如果我们想在第二个例子中输出1
的话,需要做如下的改造:
func main() {
x := 1
defer func(x int) {
fmt.Println(x) // x = 1
}(x)
x = 2
}
三、执行表达式
除了可以延迟执行函数或方法外,defer语句还可以延迟执行任意的表达式,只要它们能被求值为一个函数调用。例如:
func main() {
defer