golang中defer使用小结

清晨的麦田 · · 116 次点击 · · 开始浏览    

golang语言中defer的使用场景较多,用于锁的关闭,连接的延迟关闭等,通常在函数的结束时调用,详细的讲就是在函数结束时返回值赋值后,返回前执行defer的方法,最后才返回,另外defer确实有一定的开销,拒绝滥用。

第一点、defer 不带函数执行,defer可以理解像栈,先进后出,看下面代码

func main() {
    defer fmt.Println("one")  //第一个进入
    defer fmt.Println("two")  //第二进入
    defer fmt.Println("three") //第三个进入
}

按照先进后出的规则输出

three
two
one

Process finished with exit code 0

第二点、defer带函数的执行,defer执行在返回赋值后,返回前执行,看下面的列子

下面的列子,我们定义 一个返回值res=1,然后返回(return)res时,赋值为7,根据我们所讲,defer函数执行在返回赋值后,返回前,那么此时res为7,defer 函数执行将res赋值为8,return最后返回应该为8
看下面代码

package main

import "fmt"
func main() {
    fmt.Println(test())
}

func test() (res int) {
    res = 1
    defer func() {
        fmt.Println("start", res)
        res++
        fmt.Println("end", res)
    }()
    return 7
}

执行结果:

start 7
end 8
8

Process finished with exit code 0

第三点、defer 一定是延迟执行的么?答案是肯定的。但是有注意点需要注意

看下面列子

package main

import "fmt"

type SliceNum []int

func NewSlice() SliceNum {
    return make(SliceNum, 0)

}

func (s *SliceNum) Add(elem int) *SliceNum {
    *s = append(*s, elem)
    fmt.Println("add", elem)
    fmt.Println("add SliceNum end", s)
    return s
}
func (s *SliceNum) test(elem int) *SliceNum {
    fmt.Println("test", elem)
    fmt.Println("test", s)
    return s
}

func main() {
    s := NewSlice()
    defer s.Add(1)
    s.Add(10)

}

输出结果:
此时大家应该想到肯定是切片的加入顺序应该是10 然后 是1 结果也是这样

add 10
add SliceNum end &[10]
add 1
add SliceNum end &[10 1]

Process finished with exit code 0

读者客官可能会说,这太简单了,那么我们在defer 后面连续执行是什么效果呢?
我们修改下main函数执行的方法如下:


func main() {
    s := NewSlice()
    defer s.Add(1).Add(4)
    s.Add(11)
    s.Add(12)
    s.Add(13)
}

大家猜想下?可以先给个答案
看下执行:

add 1
add SliceNum end &[1]
add 11
add SliceNum end &[1 11]
add 12
add SliceNum end &[1 11 12]
add 13
add SliceNum end &[1 11 12 13]
add 4
add SliceNum end &[1 11 12 13 4]

Process finished with exit code 0
此时defer后面的一个方法Add(1)先执行了,然后才执行的s.Add(11),s.Add(12),s.Add(13),最后才执行了Add(4)

defer后面继续执行同样的方法或者其他方法呢?改变下我们的main函数如下:

func main() {
    s := NewSlice()
    defer s.Add(1).Add(4).Add(100).test(200)
    s.Add(11)
    s.Add(12)
    s.Add(13)
}

这个执行结果又是怎么样的?
看下结果吧:

add 1
add SliceNum end &[1]
add 4
add SliceNum end &[1 4]
add 100
add SliceNum end &[1 4 100]
add 11
add SliceNum end &[1 4 100 11]
add 12
add SliceNum end &[1 4 100 11 12]
add 13
add SliceNum end &[1 4 100 11 12 13]
test 200
test &[1 4 100 11 12 13]

Process finished with exit code 0

可以看到defer 中先执行的s.Add(1).Add(4).Add(100),然后执行s.Add(11), s.Add(12),s.Add(13),延迟执行的test函数,可以看到defer延迟执行的是最后的一个函数

如何保证整个defer是在最后执行呢?当然可以了使用 defer fun(){
}(),包住此方法

修改下main函数

func main() {
    s := NewSlice()
    defer func() {
        s.Add(1).Add(4).Add(100).test(200)
    }()//用func(){}()包住
    s.Add(11)
    s.Add(12)
    s.Add(13)
}

输出结果:

add 11
add SliceNum end &[11]
add 12
add SliceNum end &[11 12]
add 13
add SliceNum end &[11 12 13]
add 1
add SliceNum end &[11 12 13 1]
add 4
add SliceNum end &[11 12 13 1 4]
add 100
add SliceNum end &[11 12 13 1 4 100]
test 200
test &[11 12 13 1 4 100]

Process finished with exit code 0

此时,先执行s.Add(11),s.Add(12),s.Add(13),然后执行defer函数中的方法s.Add(1).Add(4).Add(100).test(200),最后输出已经按照我们想要的顺序执行了。

本文来自:简书

感谢作者:清晨的麦田

查看原文:golang中defer使用小结

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

116 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传