关于Go defer的详细使用
周伯通的麦田 人气:0先抛砖引玉defer的延迟调用:
defer特性:
1. 关键字 defer 用于注册延迟调用。 2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。 3. 多个defer语句,按先进后出的方式执行。 4. defer语句中的变量,在defer声明时就决定了。
defer用途:
1. 关闭文件句柄 2. 锁资源释放 3. 数据库连接释放
好,废话不多说,实例加深理解,我们先看看一段代码
package main import "fmt" func main() { var users [5]struct{} for i := range users { defer fmt.Println(i) } }
输出:4 3 2 1 0 ,defer 是先进后出,这个输出没啥好说的。
我们把上面的代码改下:
defer 换上闭包:
package main import "fmt" func main() { var users [5]struct{} for i := range users { defer func() { fmt.Println(i) }() } }
输出:4 4 4 4 4,很多人也包括我。预期的结果不是 4 3 2 1 0 吗?官网对defer 闭包的使用大致是这个意思:
函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4。那么 如何正常输出逾期的 4 3 2 1 0 呢?
不用闭包,换成函数:
package main import "fmt" func main() { var users [5]struct{} for i := range users { defer Print(i) } } func Print(i int) { fmt.Println(i) }
函数正常延迟输出:4 3 2 1 0。
我们再举一个可能一不小心会犯错的例子:
defer调用引用结构体函数
package main import "fmt" type Users struct { name string } func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users fmt.Println(t.name) } func main() { list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}} for _, t := range list { defer t.GetName() } }
输出:清风扬 清风扬 清风扬。
这个输出并不会像我们预计的输出:清风扬 慕容复 乔峰
可是按照前面的go defer函数中的使用说明,应该输出清风扬 慕容复 乔峰才对啊?
那我们换一种方式来调用一下
package main import "fmt" type Users struct { name string } func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users fmt.Println(t.name) } func GetName(t Users) { // 定义一个函数,名称自定义 t.GetName() // 调用结构体USers的方法GetName } func main() { list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}} for _, t := range list { defer GetName(t) } }
输出:清风扬 慕容复 乔峰。
这个时候输出的就是所谓"预期"滴了
当然,如果你不想多写一个函数,也很简单,可以像下面这样(改2处),同样会输出清风扬 慕容复 乔峰
package main import "fmt" type Users struct { name string } func (t *Users) GetName() { // 注意这里是 * 传地址 引用Users fmt.Println(t.name) } func GetName(t Users) { // 定义一个函数,名称自定义 t.GetName() // 调用结构体USers的方法GetName } func main() { list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}} for _, t := range list { t2 := t // 定义新变量t2 t赋值给t2 defer t2.GetName() } }
输出:清风扬 慕容复 乔峰。
通过以上例子,结合
我们可以得出下面的结论:
defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。但是并没有说struct这里的*指针如何处理,
通过这个例子可以看出go语言并没有把这个明确写出来的this指针(比如这里的* Users)当作参数来看待。到这里有滴朋友会说。看似多此一举的声明,
直接去掉指针调用 t *Users改成 t Users 不就行了?
package main import "fmt" type Users struct { name string } func (t Users) GetName() { // 注意这里是 * 传地址 引用Users fmt.Println(t.name) } func main() { list := []Users{{"乔峰"}, {"慕容复"}, {"清风扬"}} for _, t := range list { defer t.GetName() } }
输出:清风扬 慕容复 乔峰。这就回归到上面的 defer 函数非引用调用的示例了。所以这里我们要注意defer后面的指针函数和普通函数的调用区别。很容易混淆出错。
未完待续。。。
加载全部内容