上一篇小文,我们介绍了defer用法,但是还不是特别清楚,下面我们根据具体的代码深层次理解defer用法(只是个人理解,如有错误之处,还请大佬斧正。)
例子一
咱们直接怼代码:
package main
import "fmt"
func main() {
a := 1
defer fmt.Println("defer a=", a)
a++
fmt.Println("a++=", a)
}
结果:
a++=2
defer a=1
可见 defer 关键字的函数中如果有参数,此参数定义时就确定了。
例子二
package main
import "fmt"
func main() {
a := 1
defer fmt.Println("defer1 a=", a)
a++
defer fmt.Println("defer2 a=", a)
a++
defer fmt.Println("defer3 a=", a)
fmt.Println("a=", a)
}
结果:
a=3
defer3 a=3
defer3 a=2
defer3 a=1
可见 在进入函数调用时(比如main函数),在函数的调用栈中的栈顶根据defer的顺序依次入栈,当函数执行结束(比如main函数)即将返回(包括正常和异常)时,defer函数按照后进先出的顺序依次执行出栈。
例子三
package main
import (
"log"
"time"
)
func main() {
bigSlowOperation()
}
func bigSlowOperation() {
defer trace("bigSlowOperation")
time.Sleep(10 * time.Second)
}
func trace(msg string) func() {
start := time.Now()
log.Printf("enter %s", msg)
return func() {
log.Printf("exit %s (%s)", msg, time.Since(start))
}
}
结果:
10秒之后输出
2017/03/15 20:47:09 enter bigSlowOperation
对于这个结果大家可能都知道,先执行time.Sleep(10 * time.Second)
,程序结束执行执行defer trace(“bigSlowOperation”)。
大家看看下面的程序:
package main
import (
"log"
"time"
)
func main() {
bigSlowOperation()
}
func bigSlowOperation() {
defer trace("bigSlowOperation")()
time.Sleep(10 * time.Second)
}
func trace(msg string) func() {
start := time.Now()
log.Printf("enter %s", msg)
return func() {
log.Printf("exit %s (%s)", msg, time.Since(start))
}
}
细心的同学该发现了,只是再上面那个程序的defer trace(“bigSlowOperation”) 之后加上了(),那么加上了这个(),程序的执行结果是多少呢?
没错结果是:
2017/03/15 20:55:08 enter bigSlowOperation
2017/03/15 20:55:18 exit bigSlowOperation (10.0146469s)
没错,程序先执行trace(msg string)函数,延迟10s后defer trace()函数的返回函数 。
那么可以看到每一次bigSlowOperation被调用,程序都会记录函数的进入,退出,持续时间。(我们用time.Sleep模拟一个耗时的操作)
例子四
defer语句中的函数会在return语句更新返回值变量后再执行,又因为在函数中定义的匿名函数可以访问该函数包括返回值变量在内的所有变量,所以,对匿名函数采用defer机制,可以使其观察函数的返回值。
看下程序:
package main
import "fmt"
func main() {
double(4)
}
func double(x int) (result int) {
defer func() { fmt.Printf("double(%d) = %d\n", x, result) }()
x++
return x + x
}
程序运行结果:
double(4) = 10
例子五
对于循环关流的例子如:
for _, filename := range filenames {
if f, err = os.Open(filename ); err != nil {
return
}
f.Close()
}
如果我们使用defer关流可以使用defer特性(外函数执行完之后才执行defer 后函数的特性),那么我们把该程序改写成这样
for _, filename := range filenames {
if err := doFile(filename); err != nil {
return err
}
}
func doFile(filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
}
怎么样也许你对defer的用法可能有个大概的了解了,再加以练习更深入的了解defer了。(本人也是新手,如用理解分析不当有误指出,还请您予以指正。)