含有 defer
语句的函数,会在该函数将要返回之前,调用另一个函数。简单点说就是 defer
语句后面跟着的函数会延迟到当前函数执行完后再执行。
下面是一个简单的例子:
package main
import "fmt"
func myPrint() {
fmt.Println("Go")
}
func main() {
defer myPrint()
fmt.Println("Let's")
}
首先,执行 main
函数,因为 myPrint()
函数前有 defer
关键字,所以会在执行完 main
函数后再执行 myPrint()
函数,所以先打印出 Let's
,再执行 myPrint()
函数打印 Go
。运行该程序输出如下:
Let's
Go
上面的程序等价于下面的程序:
defer fmt.Println("Go")
fmt.Println("Let's")
即时求值的变量快照
使用 defer
只是延时调用函数,传递给函数里的变量,不应该受到后续程序的影响。
str := "Go"
defer fmt.Println(str)
str = "Let's"
fmt.Println(str)
同理,运行该程序会输出如下:
Let's
Go
延迟方法
defer
不仅能够延迟函数的执行,也能延迟方法的执行。
package main
import "fmt"
type Person struct {
firstName, lastName string
}
func (p Person) printName() {
fmt.Printf("%s %s", p.firstName, p.lastName)
}
func main() {
p := Person{"John", "Smith"}
defer p.printName()
fmt.Printf("Hello ")
}
运行该程序输出如下:
Hello John Smith
defer 栈
当一个函数内多次调用 defer
时,Go 会把 defer
调用放入到一个栈中,随后按照 后进先出(Last In First Out, LIFO) 的顺序执行。
package main
import "fmt"
func main() {
defer fmt.Printf("Caizi.")
defer fmt.Printf("am ")
defer fmt.Printf("I ")
fmt.Printf("Hello! ")
}
运行上面的程序输出如下:
Hello! I am Caizi.
defer 在 return 后调用
看看下面的例子你就知道了:
package main
import "fmt"
var x int = 100
func myfunc() int {
defer func() {x = 200}()
fmt.Println("myfunc: x =", x)
return x
}
func main() {
myx := myfunc()
fmt.Println("main: x =", x)
fmt.Println("main: myx =", myx)
}
运行该程序输出如下:
myfunc: x = 100
main: x = 200
main: myx = 100
defer 可以使代码更简洁
如果没有使用 defer
,当在一个操作资源的函数里调用多个 return
时,每次都得释放资源,你可能这样写代码:
func f() {
r := getResource() //0,获取资源
......
if ... {
r.release() //1,释放资源
return
}
......
if ... {
r.release() //2,释放资源
return
}
......
if ... {
r.release() //3,释放资源
return
}
......
r.release() //4,释放资源
return
}
有了 defer
之后,你可以简洁地写成下面这样:
func f() {
r := getResource() //0,获取资源
defer r.release() //1,释放资源
......
if ... {
...
return
}
......
if ... {
...
return
}
......
if ... {
...
return
}
......
return
}
参考文献:
[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.