golang中defer的理解
在golang当中,defer代码块会在函数调用链表中增加一个函数调用。这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用。因此,defer通常用来释放函数内部变量。
为了更好的学习defer的行为,我们首先来看下面一段代码:
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
这段代码可以运行,但存在'安全隐患'。如果调用dst, err := os.Create(dstName)失败,则函数会执行return退出运行。但之前创建的src(文件句柄)没有被释放。 上面这段代码很简单,所以我们可以一眼看出存在文件未被释放的问题。 如果我们的逻辑复杂或者代码调用过多时,这样的错误未必会被及时发现。 而使用defer则可以避免这种情况的发生,下面是使用defer的代码:
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
通过defer,我们可以在代码中优雅的关闭/清理代码中所使用的变量。defer作为golang清理变量的特性,有其独有且明确的行为。以下是defer三条使用规则。
规则一 当defer被声明时,其参数就会被实时解析
我们通过以下代码来解释这条规则:
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
上面我们说过,defer函数会在return之后被调用。那么这段函数执行完之后,是不用应该输出1呢?
读者自行编译看一下,结果输出的是0. why?
这是因为虽然我们在defer后面定义的是一个带变量的函数: fmt.Println(i). 但这个变量(i)在defer被声明的时候,就已经确定其确定的值了。 换言之,上面的代码等同于下面的代码:
func a() {
i := 0
defer fmt.Println(0) //因为i=0,所以此时就明确告诉golang在程序退出时,执行输出0的操作
i++
return
}
为了更为明确的说明这个问题,我们继续定义一个defer:
func a() {
i := 0
defer fmt.Println(i) //输出0,因为i此时就是0
i++
defer fmt.Println(i) //输出1,因为i此时就是1
return
}
通过运行结果,可以看到defer输出的值,就是定义时的值。而不是defer真正执行时的变量值(很重要,搞不清楚的话就会产生于预期不一致的结果)
但为什么是先输出1,在输出0呢? 看下面的规则二。
规则二 defer执行顺序为先进后出
当同时定义了多个defer代码块时,golang安装先定义后执行的顺序依次调用defer。不要为什么,golang就是这么定义的。我们用下面的代码加深记忆和理解:
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
在循环中,依次定义了四个defer代码块。结合规则一,我们可以明确得知每个defer代码块应该输出什么值。 安装先进后出的原则,我们可以看到依次输出了3210.
规则三 defer可以读取有名返回值
先看下面的代码:
func c() (i int) {
defer func() { i++ }()
return 1
}
输出结果是12. 在开头的时候,我们说过defer是在return调用之后才执行的。 这里需要明确的是defer代码块的作用域仍然在函数之内,结合上面的函数也就是说,defer的作用域仍然在c函数之内。因此defer仍然可以读取c函数内的变量(如果无法读取函数内变量,那又如何进行变量清除呢....)。
当执行return 1 之后,i的值就是1. 此时此刻,defer代码块开始执行,对i进行自增操作。 因此输出2.
掌握了defer以上三条使用规则,那么当我们遇到defer代码块时,就可以明确得知defer的预期结果。
golang中defer的理解的更多相关文章
- 延宕执行,妙用无穷,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中defer关键字延迟调用机制使用EP17
先行定义,延后执行.不得不佩服Go lang设计者天才的设计,事实上,defer关键字就相当于Python中的try{ ...}except{ ...}finally{...}结构设计中的finall ...
- golang中defer的使用规则
转自个人博客chinazt.cc 在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用. ...
- [转]golang中defer的使用规则
转载于:https://studygolang.com/articles/10167 在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函 ...
- GO中DEFER的理解--DEFER执行的原理
在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用.因此,defer通常用来释放函数内 ...
- go中defer的理解--defer、return、返回值之间执行顺序
defer可以读取有名返回值 func c() (i int) { defer func() { i++ }() return 1 } 输出结果是2. 在开头的时候,我们知道defer是在return ...
- Golang中defer、return、返回值之间执行顺序的坑
原文链接:https://studygolang.com/articles/4809 Go语言中延迟函数defer充当着 cry...catch 的重任,使用起来也非常简便,然而在实际应用中,很多go ...
- golang 中的struct理解
golang实验代码 package main import("fmt") type Stu struct{ name string age int } func (stu *St ...
- 【GoLang】golang 中 defer 参数的蹊跷
参考资料: http://studygolang.com/articles/7994--Defer函数调用参数的求值 golang的闭包和普通函数调用区别:http://studygolang.com ...
- golang中defer的详解 转自https://blog.csdn.net/skh2015java/article/details/77081250
Go里的defer很有用,尤其在很多执行模块化操作时,初始化时给各个需要执行的模块传入参数,但是这些参数有些事在模块执行过程中才赋值的. 这时候有了defer就不会把代码写的很凌乱. Go的defer ...
随机推荐
- jsp的服务器响应,out.println 与response.setIntHeader的报错解决方案,附:导入jar包的方法
在九大内置对象中,笔者认为用的最多的就是out.println 与response.setIntHeader,今天刚写了一个服务器响应的代码,也总是这两个报错.具体请看图. 先来说说response. ...
- ElasticSearch安装部署,基本配置(Ubuntu14.04)
ElasticSearch部署文档(Ubuntu 14.04) 安装java sudo add-apt-repository ppa:webupd8team/java sudo apt-get upd ...
- jquery trigger函数和triggerHandler函数的对照
一句话的差别就是:trigger will bubbling jQuery events (not default DOM events) and triggerHnadler will not do ...
- 无线模块 RXB12-315m TXB12-315M 应用
近期有个产品用到这对模块,因为没有什么技术支持,就直接上机调试.測试结果为带障碍发射距离60米. 下面是应用的几个知识点: 1:编程操作.事实上比較简单,调制信号315M是模块自带的,不须要调制和解调 ...
- 数学 它的内容,方法和意义 第二卷 (A. D. 亚历山大洛夫 著)
第五章 常微分方程 1. 绪论 2. 常系数线性微分方程 3. 微分方程的解及应注意的几个方面 4. 微分方程积分问题的几何解释.问题的推广 5. 微分方程解的存在性与唯一性方程的近似解 6. 奇点 ...
- Cobbler自动装机--2
自动重装工具--koan 客户机已经通过cobbler安装centos7系统完毕. 安装koan,能实现重装,安装之前先安装epel源 koan是kickstart-over-a-network的缩 ...
- Java多线程知识总结(一)
一.创建线程的三种方式: 创建线程的方式有三种,一是创建Thread实例,二是实现Runnable接口,三是实现Callable接口,Runnable接口和Callable接口的区别是一个无返回值,一 ...
- spring中ApplicationContextAware接口描述
项目中使用了大量的工厂类,采用了简单工厂模式: 通过该工厂类可以获取指定的处理器bean,这些处理器bean我们是从spring容器中获取的,如何获取,是通过实现ApplicationContextA ...
- git add -A /git add -u/git add .的用法
git的指令详解 在git中有好多的指令,但是今天这几个指令就很容易忘记而且还容易混淆 git add -u <==> git add –update 提交所有被删除和修改的文件到数据暂存 ...
- py-day1-2 python的循环语句
死循环: 条件循环: 练习 第一题: n = 1 while n < 11: if n == 7: pass else: print(n) n = n + 1 print('-----end-- ...