概述
defer是go语言的一个流程控制语句,只能在函数和方法中使用,被defer调用的函数称为延迟函数
。
格式:
defer fmt.Println("The finishing touches .")
为什么叫延长函数,有这样的规则:
- 包含defer的函数中所有语句被执行完,defer调用的函数被执行,才算此函数执行结束
- 如果有return,需要defer调用结束,才返回值
- 当出现panic时,defer被执行,才会扩散到调用方。
示例1
看起来有点像java中的finally
,在return和抛出异常后也一样会执行,可用于资源的关闭。但也不太一样,下面看一个示例:
func main() {
fmt.Println(isPositiveEventNumber(1))
}
func isPositiveEventNumber(number int) (result bool) {
defer fmt.Println("done1..")
fmt.Println("start...")
if number< 0 {
panic(errors.New("The number is a negative number!"))
}
if number%2 == 0 {
return true
}
// 匿名函数
defer func() {
fmt.Println("done2..")
}()
return
}
input: 1
start… done2.. done1.. false
input: 2
start… done1.. true
input: -1
start… panic: The number is a negative number! done1.. …
输入1时,done2在done1之前输出,它是按照相反顺序输出。输入2时,done2并没有执行,因为return执行后就结束了,defer相当于放入了一个后进先出的队列,done2没有被放入队列。同样,在输入3时,panic执行后,还要执行defer。
示例2
func main() {
recode()
}
func begin(funcName string) string {
fmt.Println("function being")
return funcName
}
func end(funcName string) string{
fmt.Println("function end")
return funcName
}
func recode() {
defer end(begin("recode"))
fmt.Println("function recode")
}
function being function recode function end
为什么会先执行begin,因为它的值作为end的参数,先求职。对于参数的问题,再看一个示例:
func main() {
for i:=0; i<5; i++ {
defer func() {
fmt.Printf("%d ",i)
}()
}
}
5 5 5 5 5
为什么输出的不是4 3 2 1 0呢?因为这个匿名函数没有参数,不会先求值。如果把i作为参数,就会得到想要的结果:
defer func(i int) {
fmt.Printf("%d ",i)
}(i)
也因此,使用要特别小心,如果延迟函数使用外部变量,最好作为参数传入。如果延迟函数return修改了外部值,有可能导致return结果的改变:
func c() (i int) {
defer func() { i++ }()
return 1
}
返回值为2。
参考书目: go并发编程实战