📜  GO语言 defer(1)

📅  最后修改于: 2023-12-03 14:41:37.161000             🧑  作者: Mango

GO语言 defer

在GO语言中,defer语句用于在函数返回前执行一些必要的清理工作。这个特性有点类似Java或C#中的finally语句。本文将详细介绍GO语言中的defer特性,其语法和使用场景。

defer语法

defer语句的语法很简单:在需要执行的语句前加上关键字defer,可以看做是一个栈,最后执行的defer语句最先被执行。如下是一个简单的例子:

package main

import "fmt"

func main() {
    defer fmt.Println("deferred")
    fmt.Println("not deferred")
}

输出结果如下:

not deferred
deferred

可以看到,如果某个函数中涉及到多个defer语句,它们会按栈的特性依次执行。

defer用法
延迟函数的参数在defer语句被调用时被执行

defer语句需要注意,延迟函数的参数在defer语句被调用时被执行,并不是在被调用函数的执行中被执行。如下是一个例子:

func test() {
    i := 1
    defer fmt.Println(i)
    i++
}

该函数输出结果为1,而不是2。这是因为i在defer语句被执行的时候被求值。

defer用于资源释放

defer一个函数最常用的场景是处理共享的资源,如文件、数据库连接、套接字和格式化输出等。当然,难免有些程序员都用了defer所代表的的作用(清理资源)却没有真正的清理掉它们。

package main

import "fmt"

func readwrite() {
    f, _ := os.Create("test.txt")
    defer f.Close()

    fmt.Fprintln(f, "Hello, world!")
    f.Read(...)
}

f.Close()这个语句会在这个函数结束前被执行。这种模式可以应用于在函数进入时初始化一个资源,然后在函数返回或发生异常时,通过defer语句释放该资源。这种模式可以避免因忘记释放资源而导致的问题。

defer语句在panic后仍被执行

defer语句在函数返回或panic时被执行,这也是一种处理错误或异常的机制。如果函数中已有其他执行返回或发生错误的路径,那么在这个路径的末尾增加一个defer语句也会让代码变得更加容易维护。下面是一个例子:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    return io.Copy(dst, src)
}

如果从srcName打开文件失败,CopyFile函数会直接返回并跳过后面的两个defer。如果在创建dstName文件时失败,则只会执行第一个defer语句,并关闭了src文件。如果拷贝操作发生错误并返回,那么两个文件都会安全地关闭。

总结

defer是GO语言中一个很好的特性,可以使得代码变得简洁并避免一些类似资源处理的错误。在本文中,我们介绍了defer的语法和用法,并给出了一些使用场景供读者参考。