之前一直对Go中的defer不太理解,所以我单独弄出来整理一下。
在golang当中,defer代码块会在函数调用链表中增加一个函数调用。这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用。因此,defer通常用来释放函数内部变量。
通过defer,我们可以在代码中优雅的关闭/清理代码中所使用的变量。defer作为golang清理变量的特性,有其独有且明确的行为。以下是defer三条使用规则。

规则一 当defer被声明时,其参数就会被实时解析

我们通过以下代码来解释这条规则:

func a() {i := 0defer fmt.Println(i)i++return
}

运行结果是0
这是因为虽然我们在defer后面定义的是一个带变量的函数: fmt.Println(i). 但这个变量(i)在defer被声明的时候,就已经确定其确定的值了。 换言之,上面的代码等同于下面的代码:

func a() {i := 0defer fmt.Println(0) //因为i=0,所以此时就明确告诉golang在程序退出时,执行输出0的操作i++return
}

为了更为明确的说明这个问题,我们继续定义一个defer:

func a() {i := 0defer fmt.Println(i) //输出0,因为i此时就是0i++defer fmt.Println(i) //输出1,因为i此时就是1return
}

通过运行结果,可以看到defer输出的值,就是定义时的值。而不是defer真正执行时的变量值(很重要,搞不清楚的话就会产生于预期不一致的结果)

再看一个例子:

package mainimport "fmt"
func f1() (result int) {defer func() {result++}()return 0
}func f2() (r int) {t := 5defer func() {t = t+5}()return t
}func f3() (t int) {t = 5defer func() {t = t+5}()return t
}
func f4() (r int) {defer func(r int) {r = r + 5}(r)return 1
}func main() {fmt.Println(f1())fmt.Println(f2())fmt.Println(f3())fmt.Println(f4())
}

运行结果是:

1
5
10
1

函数返回的过程是这样子的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。

defer表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值,使最终的函数返回值与你想象的不一致。

可以将return xxx改成
返回值=xxx
调用defer函数
空的return

那上面的例子就可以改成:

func f11() (result int) {result = 0   //先给返回值赋值func(){               //再执行defer 函数result++}()return                //最后返回
}func f22() (r int) {t := 5r = t //赋值指令func() {  //defer 函数被插入到赋值与返回之间执行,这个例子中返回值r没有被修改t = t+5}return   //返回
}func f33() (t int) {t = 5    //赋值指令func(){t = t+5  //然后执行defer函数,t值被修改}return
}
func f44() (r int) {r = 1    //给返回值赋值func(r int){   //这里的r传值进去的,是原来r的copy,不会改变要返回的那个r值r = r+5}(r)return
}

规则二 defer执行顺序为先进后出

当同时定义了多个defer代码块时,golang安装先定义后执行的顺序依次调用defer。不要为什么,golang就是这么定义的。我们用下面的代码加深记忆和理解:

func b() {for i := 0; i < 4; i++ {defer fmt.Print(i)}
}

在循环中,依次定义了四个defer代码块。结合规则一,我们可以明确得知每个defer代码块应该输出什么值。 安装先进后出的原则,我们可以看到依次输出了3210.

再看之前的那个例子:

package mainimport "fmt"func main() {fmt.Println("a return:", a()) // 打印结果为 a return: 0
}func a() int {var i intdefer func() {i++fmt.Println("a defer2:", i) // 打印结果为 a defer2: 2}()defer func() {i++fmt.Println("a defer1:", i) // 打印结果为 a defer1: 1}()return i
}

结果是:

a defer1: 1
a defer2: 2
a return: 0

规则三 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.

看这个例子:

package mainimport "fmt"
func trace(s string) string {fmt.Println("entering:",s)return s
}func un(s string) {fmt.Println("leaving:",s)
}func a() {defer un(trace("a"))fmt.Println("in a")
}func b() {defer un(trace("b"))fmt.Println("in b")a()
}func main() {b()
}

运行结果是:

entering: b
in b
entering: a
in a
leaving: a
leaving: b

理解Golang中defer的使用相关推荐

  1. Go实战--golang中defer的使用

    原址 生命不止,继续 go go go !!! 学习golang这么久了,还没看到类似传统的 try-catch-finally 这种异常捕捉方式.  但是,Go中引入的Exception处理:def ...

  2. 理解Golang中的nil

    参考: 有趣的面试题:Go语言中的nil比较 - 知乎 (zhihu.com) 理解Go语言的nil - 简书 (jianshu.com) Golang中的nil,没有人比我更懂nil! - 知乎 ( ...

  3. 深入理解golang的defer

    defer 估计是每个 Gopher 每天写代码都会写,那么你是不是真正的理解了 defer 呢?不妨看一下下面这个代码片段,这个是我之前给 UC 那边一个 team 做 Golang 培训的时候想的 ...

  4. 深入理解Golang中的Context包

    context.Context是Go语言中独特的设计,在其他编程语言中我们很少见到类似的概念.context.Context深度支持Golang的高并发. 1. Goroutine和Channel 在 ...

  5. golang 切片 接口_如何理解Golang中的接口?

    个人认为,要理解 Go 的接口,一定先了解下鸭子模型. 鸭子模型 那什么鸭子模型? 鸭子模型的解释,通常会用了一个非常有趣的例子,一个东西究竟是不是鸭子,取决于它的能力.游泳起来像鸭子.叫起来也像鸭子 ...

  6. golang中defer语句使用小结

    defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源.关闭数据库连接.断开socket连接.解锁一个加锁的资源.Go语言机制担保一定会执行defer语句中的代 ...

  7. 在golang中defer、panic与recover的作用

    package mainimport "fmt"func main() {var s strings = "panic"fmt.Printf("a的初 ...

  8. Golang中defer的执行时机

    1.defer的执行顺序 底层是采用栈 func func1() {fmt.Println("A") }func func2() {fmt.Println("B" ...

  9. 理解golang中什么是nil

    nil是什么 // nil is a predeclared identifier representing the zero value for a // pointer, channel, fun ...

最新文章

  1. OPENGFILER存储柜
  2. ibatis基础(三):查询指定id的单个对象
  3. 看了2022华为春季发布会
  4. 网站的iphone版快开发完了
  5. 马云再谈 996:真正的 996 与被剥削无关
  6. MATLAB语言初步学习(二)
  7. css就近原则_CSS 三大特性
  8. 中文字体下载大全+传世书法墨迹珍藏..等20款
  9. 贝壳找房原生爬虫租房分析
  10. 十一青岛2人4日自助游记
  11. Vue之如何调用高德地图步骤详解
  12. 大一计算机基础试题答案,计算机基础知识试题及答案-(1).doc
  13. 基于光流传感器定位和导航的自主飞行无人机
  14. 渗透学习-文件上传篇-基础知识部分(持续更新中)
  15. cocos creator 模拟重力爆炸效果
  16. 未成年帐号登录华为游戏,启动后不断弹出防沉迷提示
  17. 鸿蒙之境法有三乘,神都夜行录法有三乘副本组队通关攻略-神都夜行录法有三乘副本组队怎么过_牛游戏网...
  18. 2020年需要学习的十大按需编程语言
  19. Selenium IDE使用指南四(代码导出)
  20. 适用于磁场中的传感器

热门文章

  1. 如何实现微信公众号自动回复
  2. 【例4-6】香甜的黄油
  3. linux检测更新文件系统,fsck-Linux文件系统检查工具介绍
  4. 安装纯净版Windows7系统
  5. XTU OJ 128题 A+B IV
  6. 四叶草的python代码_python学习
  7. 【转载】SSH服务器端/etc/ssh/sshd_conf配置文件详解
  8. LWN: folio 改动未能合入!
  9. Vue中使用百度地图——设置地图标注
  10. 液晶拼接屏安装过程有何要求?