Go语言 defer

导言

  • 原文链接: Part 29: Defer
  • If translation is not allowed, please leave me in the comment area and I will delete it as soon as possible.

defer

defer 是什么?

defer 是一条语句,通过这条语句,我们可以延迟函数的执行。

上面的定义有些抽象,但实际上它非常简单,来看看例子:

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

在上面的程序中,largest函数 用于寻找切片中的最大值。lastest函数 的第一行是 defer finished() — 这表示在 lastest函数 结束前,调用 finished函数。

运行这个程序,你会得到如下输出:

Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest  

在上面的输出中,前 2 句是 largest函数 打印的,而最后一句是 largest函数 退出前,finished函数 打印的。


延迟 方法的执行

defer 不仅能用于延迟函数的执行,它还能延迟方法的执行。

看看下面的程序:

package main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}

在上面程序的第 22 行,我们延迟了方法的执行。

程序输出如下:

Welcome John Smith  

参数确定

defer 语句定义时,编译器会确定 被延迟函数的参数。

通过例子,理解上面的意思:

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

在第 12 行定义 defer语句 时,此时 a 等于 5,所以 被延迟函数printA 的参数就为 5
在第 13 行,我们将 a 改为 10,并在下一行输出 a

最终,程序输出如下:

value of a before deferred function call 10  
value of a in deferred function 5  

从上面可以看出:在 defer语句 定义后,将 a 改为 10 并不会影响 printA函数 的参数。


延迟栈

当函数内定义多个 defer 时,这些 defer 会被推入栈中,并以 后进先出 的顺序执行。

接下来,我们来写个程序吧,它的功能是:使用 defer,将字符串反向输出。

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Original String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

在上面的程序中,for range循环 遍历了整个字符串,并调用 defer fmt.Printf("%c", v)语句。

这些 defer 会形成一个栈,内部如下:
在这里插入图片描述
栈是后进先出的数据结构,所以最晚入栈的 defer 最早执行。

对应于上面的例子,此时 defer fmt.Printf("%c", 'n') 会最先执行。

程序输出如下:

Original String: Naveen  
Reversed String: neevaN 

defer 的实际用途

这一节,我们来谈谈 defer 的实际用途。

当无论代码流如何,我们都需要执行特定的函数,defer 就派上用场了。

接下来,我们通过例子进行理解。
我会先写一个没有 defer 的程序,之后使用 defer 对其进行重写。在这个过程中,我们就能明白 defer 的用处了。

没用 defer 的代码:

package main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {  
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        wg.Done()
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        wg.Done()
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
    wg.Done()
}

func main() {  
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

从上面的代码中,你会发现:在 area 函数结束前,我们都调用了 wg.Done()

接下来,我们使用 defer 改写上面的程序。

package main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {  
    defer wg.Done()
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
}

func main() {  
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

在上面的程序中,我们移除了 3wg.Done(),转而用 1defer wg.Done() 替代。这使得代码更简单易懂。

程序输出如下:

rect {8 9}'s area 72  
rect {-67 89}'s length should be greater than zero  
rect {5 -67}'s width should be greater than zero  
All go routines finished executing  

对于上面的程序,使用 defer 还有一个好处。
好处是:假设我们想给 area方法 添加新的 if判断。此时,如果没有使用 defer,我们每次都要在 if语句块 的最后添加 wg.Done(),而如果使用 defer,我们就不用这样做了。

这就是 defer 了~

祝你愉快~


原作者留言

优质内容来之不易,您可以通过该 链接 为我捐赠。


最后

感谢原作者的优质内容。

欢迎指出文中的任何错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值