Golang中多用途的defer

Golang中多用途的defer,第1张

概述defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢? A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function returns, Golang的官方时这么定义的。 1.那么在什么情况下会调用defer延迟过的函数

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

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

Golang的官方时这么定义的。

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

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

当函数执行了return 语句后

当函数处于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修饰过的函数调用的返回值时丢弃的,因此不要想着再去使用其返回值。本来也没有地方去使用。

总结

以上是内存溢出为你收集整理的Golang中多用途的defer全部内容,希望文章能够帮你解决Golang中多用途的defer所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1290810.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-09
下一篇 2022-06-09

发表评论

登录后才能评论

评论列表(0条)

保存