热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang中多用途的defer

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢?Adeferstatementinvokesafunctionwhoseexecu

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢?

A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function returns,

Golang的官方时这么定义的。

1.那么在什么情况下会调用defer延迟过的函数呢?

从文档中可以知道主要有两种情况:

  1. 当函数执行了return 语句后

  2. 当函数处于panicing状态,也就是程序中panic回溯上来到当前函数范围内时

2. 如何使用defer呢?

defer可以当作一个修饰符,其不是用来修饰函数定义的,而是用来修饰函数调用的。当你调用一个函数时,在正常的书写方式的前面加上 defer 之后,该函数调用就被延迟了,具体我们来看个例子:

func TestPanic(){    defer func (){fmt.Println("defer function be invoked")}()    fmt.Println("The first one to be invoke")    return}

运行后会发现结果为:

The first one to be invokedefer function be invoked

这里我们用到了一个匿名函数,但是其原型就是 fun() 形式的调用。这里符合上面的在return 语句之后执行了我们defer过的函数。

3.defer主要用于什么场景

defer最主要的使用场景有两个,一个时资源的释放,如关闭打开的文件,另一个是和panic 以及 recover 组合起来模拟try...catch 功能;。除此之外defer还可以用来对return命名返回值作修改。

资源释放当我们打开文件作操作后,很容易忘记释放文件资源。假设现在B资源要依赖A资源。如下:

resA, err:= getA()if err!=nil {    return}resB,err := getB(resA)if err != nile {    return}

这里我们就容易产生bug,在获取B资源出错时,没有释放获取成功的A资源。这里我们添加:

resA, err:= getA()defer ReleaseA()if err!=nil {    return}resB,err := getB(resA)defer ReleaseB()if err != nile {    return}

就可以保证,无论时在哪个位置返回,其上的资源都会得到释放。

  • 异常处理defer还有最大的用处就是和panic以及recover组合成try...catch结构。

看个示例,来自官网的:

func protect(g func()) {    defer func() {        log.Println("done")  // Println executes normally even if there is a panic        if x := recover(); x != nil {            log.Printf("run time panic: %v", x)        }    }()    log.Println("start")    g()}

当g()中通过panic抛出错误时,会在defer中用recover进行捕获。也就是在子函数中的panic触发了其处于panicing状态,从而当panic回溯到当前函数时调用本函数的defer修饰的函数。

当然这里把g()替换成panic()也是可行的,就没有panic回溯了,直接时本函数中的panic触发使其处在panicing状态,从而调用defer修饰的函数。

  • 修改返回值

如同上面的例子,如果对于有异常和没有异常返回不同的值,那么该如何操作。传统的方法是:

try {    ...} catch (exception1){    return 1;}return 2...

由于Golang中不能在defer里面返回值,所以我们不能用上面的逻辑,在主函数和defer里面返回不同的结果。这个时候我们可以借助命名返回值来帮忙。在Golang中使用

func InvokeF()(rst int){    defer func(){        if err:=recover();err!=nil {            if err== exception1 {                rst = -1            }        }    }()    rst = f()    return}

当正常时,返回的rst为f()调用结果,当出现异常时,rst的值会被修改,从而达到目的。

4.defer中的坑

  • 栈式调用

defer的调用顺序时栈式的,也就是后修饰的函数会被先调用。如果熟悉进程和线程API的化,这里defer的调用就和at_exit()有点类似,在退出时栈式调用注册过的退出函数。在使用中我们会发现这样的方式是符合我们代码原意的。假设A资源依赖B资源。那么我们这样:

resA ,err := getA()defer releaseA()...resB,err := getB(resA)defer releaseB()

一来,我们在获取资源时就注册defer,语义上跟符合人类语言。二来,当A资源获取失败时仅释放A的环境;当B资源获取失败时,释放B和A的资源,且先释放依赖A的B。这样代码更为整洁易读。

  • 出现时计算

首先要注意的是,defer修饰的函数调用中如果有参数,那么参数取的是当前计算时的值,而不是在return或者panic时该变量的值。

func main() {    i := 2;    deferFun := func (i int){fmt.Printf("i in panic is %d \n",i)}    defer deferFun(i+1)    i += 12    fmt.Printf("i befor return is %d \n",i)    return}

得到结果为:

ibeforreturnis14iinpanicis3

注意,这里只是执行了参数的计算,而并没有执行函数体,函数体时在return之后执行的。

  • 丢弃返回值

defer修饰过的函数调用的返回值时丢弃的,因此不要想着再去使用其返回值。本来也没有地方去使用。


本文出自 “Done_in_72_hours” 博客,请务必保留此出处http://gotaly.blog.51cto.com/8861157/1413402


推荐阅读
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
author-avatar
爱昵宝贝H33_568
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有