go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.我们先来看几个例子.

例一: defer 是先进后出

  这个很自然,后面的语句会依赖前面的资源,因此如果先前面的资源先释放了,后面的语句就没法玩了.

 func main() {
var whatever []struct{} for i := range whatever {
defer fmt.Println(i)
}
}

这个输出应该很明显,就是4 3 2 1 0

例二: defer 碰上闭包

func main() {
var whatever []struct{}
for i := range whatever {
defer func() { fmt.Println(i) }()
} }

这个输出可能会超出某些人的意料,结果是4 4 4 4 4

其实go说的很清楚,我们一起来看看go spec如何说的

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.

也就是说函数正常执行,由于闭包用到的变量i在执行的时候已经变成4,所以输出全都是4.

例三: defer f.Close

这个大家用的都很频繁,但是go语言编程举了一个可能一不小心会犯错的例子.

type Test struct {
name string
}
func (t * Test) Close(){
fmt.Println(t.name," closed");
}
func main(){
ts:=[]Test{{"a"},{"b"},{"c"}}
for _,t := range ts{
defer t.Close()
}
}

这个输出并不会像我们预计的输出c b a,而是输出c c c

可是按照前面的go spec中的说明,应该输出c b a才对啊.

那我们换一种方式来调用一下.

例四: 像例一一样的调用

type Test struct {
name string
}
func (t * Test) Close(){
fmt.Println(t.name," closed");
}
func Close(t Test){
t.Close()
}
func main(){
ts:=[]Test{{"a"},{"b"},{"c"}}
for _,t := range ts{
Close(t)
}
}

这个时候输出的就是c b a

当然,如果你不想多写一个函数,也很简单,可以像下面这样,同样会输出c b a

例五:看似多此一举的声明

type Test struct {
name string
}
func (t * Test) Close(){
fmt.Println(t.name," closed");
}
func main(){
ts:=[]Test{{"a"},{"b"},{"c"}}
for _,t := range ts{
t2:=t
t2.Close()
}
}

通过以上例子,结合

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.

这句话.可以得出下面的结论:

defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行.也就是复制了一份.但是并没有说struct这里的this指针如何处理,通过这个例子可以看出go语言并没有把这个明确写出来的this指针当作参数来看待.

go语言 defer 你不知道的秘密!的更多相关文章

  1. C语言星号的秘密

    C语言星号的秘密 星号的秘密 1.乘法运算符   2.定义指针 int *p = 0; 还是 int* p = 0;? 后一种比较容易这样理解:定义了一个变量p,它是指针型的(更详细一点,是指向int ...

  2. go语言 defer 高级

    go语言defer语句的用法 defer的语法 defer后面必须是函数调用语句,不能是其他语句,否则编译器会出错. package main import "log" func ...

  3. go语言---defer

    go语言---defer https://blog.csdn.net/cyk2396/article/details/78885135 defer 是在函数退出前调用,多个defer遵循 先进后出 的 ...

  4. go语言defer使用

    defer Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句.当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回.特别是当你在进行一些打开资源 ...

  5. go语言defer关键字背后的实现,语法,用法

    原文: https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html ------------------------------ ...

  6. 探究 Go 语言 defer 语句的三种机制

    Golang 的 1.13 版本 与 1.14 版本对 defer 进行了两次优化,使得 defer 的性能开销在大部分场景下都得到大幅降低,其中到底经历了什么原理? 这是因为这两个版本对 defer ...

  7. 深入 Go 语言 defer 实现原理

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/523 本文使用的go的源码 1.15.7 介绍 defer 执行规 ...

  8. go语言defer panic recover用法总结

    defer defer是go提供的一种资源处理的方式.defer的用法遵循3个原则 在defer表达式被运算的同时,defer函数的参数也会被运算.如下defer的表达式println运算的同时,其入 ...

  9. Go 语言defer用法

    defer延迟调用: 1.确保调用在函数结束时发生: 2.defer列表为先进后出: 3.通常在Open/Close  Lock/Unlock中使用. defer调用顺序示例: package mai ...

随机推荐

  1. 实战 TestNG 监听器

    TestNG 是一个开源的自动化测试框架,其灵感来自 JUnit 和 NUnit,但它引入了一些新功能,使其功能更强大,更易于使用.TestNG 的设计目标是能够被用于进行各种类型测试:单元测试.功能 ...

  2. 参数传递中编码问题(Get/Post 方式)(二)

    form有2中方法把数据提交给服务器,get 和post ,分别说下吧.(一)get 提交1.首先说下客户端(浏览器)的form表单用get 方法是如何将数据编码后提交给服务器端的吧. 对于get 方 ...

  3. Py修行路 python基础 (十一)迭代器 与 生成器

    一.什么是迭代? 迭代通俗的讲就是一个遍历重复的过程. 维基百科中 迭代(Iteration) 的一个通用概念是:重复某个过程的行为,这个过程中的每次重复称为一次迭代.具体对应到Python编程中就是 ...

  4. selenium -文件上传的实现 -对于含有input element的上传

    使用selenium做自动化时,我们经常会遇到的一个让人头疼的问题就是文件上传. 问题的难点在于selenium无法识别并操作Windows窗口,若我们可以绕过弹出框直接把文件信息上传给选择按钮,难点 ...

  5. Cygwin windows10上安装出现系列问题及解决方法

    问题1描述: 发现vim不好使,Backspace键只是前移,不能删除,按方向键更是按出ABCD来.   解决方法: $ cp /usr/share/vim/vim73/vimrc_example.v ...

  6. leetcode481

    public class Solution { public int MagicalString(int n) { ) ; ) ; ]; a[] = ; a[] = ; a[] = ; , tail ...

  7. sql server生成递归日期、连续数据

    WITH Date AS ( SELECT CAST('2008-08-01' AS DATETIME) da UNION ALL FROM Date WHERE da < '2008-08-2 ...

  8. 通过window.crypto.getRandomValues获得一个大于零的随机数

    window.crypto.getRandomValues(new Uint32Array(1))[0]; 浏览器支持情况如下: IE: no IE Mobile: no Firefox24+ Fir ...

  9. 如何在Linux中添加新的系统调用

    系统调用是应用程序和操作系统内核之间的功能接口.其主要目的是使得用户 可以使用操作系统提供的有关设备管理.输入/输入系统.文件系统和进程控制. 通信以及存储管理等方面的功能,而不必了解系统程序的内部结 ...

  10. Java基础知识(二)之控制语句

    1.条件运算符   ⑴if...else... ⑵三目表达式——X?Y:Z 当X为真时,结果为Y:反之,为Z. ⑶switch(表达式){ case 1:    执行代码块 1; break: cas ...