Go里的defer很有用,尤其在很多执行模块化操作时,初始化时给各个需要执行的模块传入参数,但是这些参数有些事在模块执行过程中才赋值的。

这时候有了defer就不会把代码写的很凌乱。

Go的defer语句用来调度一个函数调用(被延期的函数),使其在执行defer的函数即将返回之前才被运行,被延期执行的函数,它的参数(包括接受者)实在defer执行的时候被求值的,而不是在调用执行的时候。也就是说被延期执行的函数的参数是按正常顺序被求值的。

defer会按逆序执行

defer是Go语言提供的关键字,常用来释放资源,会在函数返回之前进行调用。如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。defer 函数调用的执行时机是外层函数设置返回值之后, 并且在即将返回之前。

例1:

func main() {
         for i:=0 ;i<5;i++{
              defer fmt.Printf("%d",i)
              fmt.Println("bbbbb")
         }
         fmt.Println("aaaaa")
}

执行结果:

bbbbb

bbbbb

bbbbb

bbbbb

bbbbb

aaaaa

43210

例2:

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

例3

func f1() (result int) {
       defer func() {
              result++
       }()
       return 0
}

func f2() (r int) {
       t := 5
       defer func() {
              t = t+5
       }()
       return t
}

func f3() (t int) {
       t = 5
       defer func() {
              t = t+5
       }()
       return t
}

func f4() (r int) {
       defer func(r int) {
              r = r + 5
       }(r)
       return 1
}

要使用defer不踩坑,最重要的一点就是明白,return xxx不是一条原子指令

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

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

可以将return xxx改成

返回值=xxx

调用defer函数

空的return

例3可以改写成这样

func f11() (result int) {
       result = 0   //先给返回值赋值
       func(){               //再执行defer 函数
              result++
       }()
       return                //最后返回
}

func f22() (r int) {
       t := 5
       r = 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
}

参考:《Effective Go》、《深入解析go内核实现》

golang中defer的详解 转自https://blog.csdn.net/skh2015java/article/details/77081250的更多相关文章

  1. golang语言并发与并行——goroutine和channel的详细理解(一) 转发自https://blog.csdn.net/skh2015java/article/details/60330785

    如果不是我对真正并行的线程的追求,就不会认识到Go有多么的迷人. Go语言从语言层面上就支持了并发,这与其他语言大不一样,不像以前我们要用Thread库 来新建线程,还要用线程安全的队列库来共享数据. ...

  2. Window 对象详解 转自 http://blog.csdn.net/jcx5083761/article/details/41243697

    详解HTML中的window对象和document对象 标签: HTMLwindowdocument 2014-11-18 11:03 5884人阅读 评论(0) 收藏 举报 分类: HTML& ...

  3. 28 Corn表达式详解 (转自http://blog.csdn.net/claram/article/details/51785193)

    Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: Seconds Minutes Hours DayofMonth Month ...

  4. .net反射详解 原文://http://blog.csdn.net/wenyan07/article/details/27882363

    概述反射 通过反射可以提供类型信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象. 反射机制允许程序在执行过程中动态地添加各种功能. 运行时类型标识 运行时类型标识(RTTI),可以在程 ...

  5. springboot项目--传入参数校验-----SpringBoot开发详解(五)--Controller接收参数以及参数校验----https://blog.csdn.net/qq_31001665/article/details/71075743

    https://blog.csdn.net/qq_31001665/article/details/71075743 springboot项目--传入参数校验-----SpringBoot开发详解(五 ...

  6. 虚拟机中的CentOS7如何上网?---https://blog.csdn.net/nothing2017/article/details/61420767

    虚拟机中的CentOS7如何上网?https://blog.csdn.net/nothing2017/article/details/61420767

  7. Golang拼接字符串的5种方法及其效率_Chrispink-CSDN博客_golang 字符串拼接效率 https://blog.csdn.net/m0_37422289/article/details/103362740

    Different ways to concatenate two strings in Golang - GeeksforGeeks https://www.geeksforgeeks.org/di ...

  8. 向txt文件中写入内容(覆盖重写与在末尾续写+FileOutputStream与FileWriter)(转发:https://blog.csdn.net/bestcxx/article/details/51381460)

    !!!! 读取txt文件中的内容 import java.io.BufferedReader; import java.io.File; import java.io.FileReader; /** ...

  9. Redis中为什么使用跳表---------转自http://blog.csdn.net/u010412301/article/details/64923131

    最近在研究数据库的一些底层实现,百度的面试官问到了跳表,当时没有回答上来,在csdn上看到了这篇文章,感觉写的比较好,希望大家可以多多交流. Redis里面使用skiplist是为了实现sorted ...

随机推荐

  1. 4--Python入门--Python数据集合类型--集合

    在基础数据类型的基础上,Python有6中数据集合的类型: 列表list,最常用的数据类型,以[]为标识 元组tuple,和list很相似,但是不能二次赋值,用()标识 集合set,和list类似,但 ...

  2. Python基础3切片,字符串的方法,for 循环

    切片:截取序列(字符串,列表,元组等)中某一段字符,并不改变原数据和数据类型.结构:[起始位置:终止位置:步长]  但不包括终止位置.所谓:顾头不顾尾. 索引:序列中每个元素都是有编号的,都是从0开始 ...

  3. [转]Ubuntu安装Python3.6

    Ubuntu安装Python3.6   Ubuntu默认安装了Python2.7和3.5 输入命令python

  4. 九度OJ1108-堆栈的使用

    题目1108:堆栈的使用 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:11620 解决:3372 题目描述: 堆栈是一种基本的数据结构.堆栈具有两种基本操作方式,push 和 pop.P ...

  5. stringify在苹果电脑下的值不能为空

     sessionStorage.channel = JSON.stringify(  );苹果的safari不接受stringify里面为空 火桑飘零ご 2018/1/25 20:21:49 wind ...

  6. NET Core MVC中创建PDF

    使用Rotativa在ASP.NET Core MVC中创建PDF 在本文中,我们将学习如何使用Rotativa.AspNetCore工具从ASP.NET Core中的视图创建PDF.如果您使用ASP ...

  7. 《DSP using MATLAB》Problem 6.12

    代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...

  8. css与html 与js的基础语法

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Sample_2-23456.a ...

  9. java-IO流(File对象-深度遍历指定目录下的文件夹和文件)

    需求:遍历这个树状结构 File(String pathname) '\\'为了转义'\' // 通过抽象路径pathname 创建一个新的文件或者目录 File parent = new File( ...

  10. malloc,calloc,alloca和free函数

    void *malloc(size_t size)因为返回类型为空,所以可以赋值到任何类型指针,其分配的空间大小为size,返回新分配内存地址的起始处的指针,其所分配的内存未经初始化,若分配失败返回N ...