Go语言特性——defer的使用方法及其注意事项

本篇文章对Go语言的一个重要特性——defer进行了简单的介绍,并表明了使用defer过程中需要注意的事项

1. defer的含义与作用

对于从其他语言切换过来的朋友们,在第一次接触defer时或许会和我一样感到奇怪又惊喜。首先必须明确一点:defer用于推迟函数的执行——直到调用defer的函数结束时()。这里有几个关键点我们一个一个来看:

  • 推迟:推迟意味着在出现defer关键词时,其后紧跟的函数并不会立马执行,而是会被推迟——这与常规的顺序执行流程存在差异需要关注
  • 调用defer的函数:在defer出现时一定存在两个主体,一个是调用defer的函数(也就是defer出现的scope所属的函数),另一个则是defer所指的函数
  • “前”:被推迟的函数会在该scope结束前被执行
  • 作用:
    • 由于defer的特性,defer常被用于对关键资源的管理,例如关闭操作——对文件、套接字等需要执行闭合操作的对象可以在创建之后立刻通过defer做出关闭指示,避免后期忘记导致资源泄露等问题
    • 承接上一个作用,由于对象的创建与关闭被组织在一小段程序中,增加了程序的可读性

2. defer的使用示例

  • 下面展示一个最简单的defer使用示例:
package main

import "fmt"

func main() {
	defer func() {
			fmt.Println("This is the first!")
		}() // 这里表示调用
	fmt.Println("This is the second!")
}

输出为:

This is the second!
This is the first!
  • 通过上述的简单小例子,大家应该能够体会defer的作用

3. defer函数的执行顺序为FILO

  • 如果出现了多个defer functions,那么各个function执行的顺序是FILO(first in last out)——也就是说第一个defer的函数在最后一个被执行,下面我们通过一个小例子来进行说明:
package main

import "fmt"

func main() {
	for i:=0; i<5; i++ {
		defer func(index int) { fmt.Println(index) }(i)
	}
}

输出为:

4
3
2
1
0
  • 上述的小示例中以供出现了5个defered function,它们会输出对应的i,从顺序执行的角度来看,应该是以01234的顺序激活的各个函数,但是最终的输出却是43210,由此可以说明FILO的含义

4. 被defer作用的函数其参数是在defer时确定而不是在执行时确定

  • 关于对这一个小点的说明,细心的小伙伴应该在第3小节就已经感到异常了,注意:虽然我们顺序激活了5个defer function,但是它们的参数都是 i ,而 i 在最终函数结束时应该为5,但是各个defered functions却仍然正确地输出了 i 的值,这在一定程度上说明了defer function的参数是在defer时确定的而不是在函数最终执行时确定的
  • 下面我们再看一个更直观的小例子:
package main

import "fmt"

func main() {
	s := "Hello World!!"
	defer func(sentence string) { fmt.Println(sentence) } (s)
	s = "Hello Golang!!"
}

输出为:

Hello World!!

上面的小例子非常的直观,初始时s的值为“Hello World!!”,紧接着我们defer了一个函数,并将s当做该函数的参数传递给它;之后我们在该函数执行前更改了s的值“Hello Golang!!”,最终函数执行,输出的却仍是之前的s值。其实看到这里大家应该也会和我一样想到一个更常见的特性——“闭包”,虽然有些差异但大家也可以联想记忆一下,总之都是记住其执行环境而不会丢失

  • 下面我们来看一个复杂一些的例子(这个例子来源于官方文档),咱们一起来分析一下:
package main

import "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

大家可以尝试自己先分析一下再来看我的解析:

  • 共定义了四个功能函数,traceuna以及b
  • 主函数为main,其调用了b
  • b首先defer了un——defer时要确定该函数的参数值,该参数值为trace("b")——trace执行,输出① “entering: b” 并返回“b”
  • b接着输出②“in b”
  • 执行a——a中defer了un——defer时要确定该函数的参数值,该参数值为trace("a")——trace执行,输出③“entering: a”并返回“a”
  • a接着输出④“in a”
  • a即将结束,执行defer函数un("a")——输出⑤“leaving: a”——a退出返回b
  • b即将结束,执行defer函数un("b")——输出⑥“leaving: b”——b退出返回main
  • main结束执行——程序结束
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值