在本教程中,您将学习 Go 编程语言中的 defer 语句。从上一个教程中,您学习了像 if、switch、for 循环、for –range 循环这样的 控制流语句,这些语句在其他编程语言中非常常见。但 Golang 中的 defer 并不像其他控制流语句那样常见。
defer 这个词的意思是“推迟(一项行动或事件)到稍后或推迟”。在 Go 编程语言中,带有 defer 关键字的语句指定了一个语句需要被推迟或稍后执行,但应该在退出主循环之前执行。
defer 关键字推迟执行语句,直到调用函数结束。它被实现为一个栈数据结构,除了 defer 关键字函数调用之外,所有函数都以后进先出(LIFO)的方式在栈上调用。它仅在最后一个函数调用返回后才被调用。
在正常的 Go 应用程序或程序中,控制流从我们调用的任何函数的顶部流向底部。通常,一个函数从第一行开始执行,直到最后一行。
让我们通过一个程序来理解这一点。当我们运行下面的代码时,它会按顺序运行并输出“first”、“second”、“third”。
package main
import "fmt"
func main() {
fmt.Println("First")
fmt.Println("Second")
fmt.Println("Third")
}
输出
First Second Third
defer 关键字用于推迟语句的执行。假设将 defer 关键字放在 fmt.Println("Second") 前面,您会注意到 second 将在 third 之后打印。
package main
import "fmt"
func main() {
fmt.Println("First")
defer fmt.Println("Second") //defer keyword
fmt.Println("Third")
}
输出
First Second Third
defer 关键字在函数完成最后一条语句后,但在函数实际返回之前执行传递给它的任何函数。所以,main 函数 () 的执行方式是:它打印上面示例中的第一条语句的输出“first”。然后,在下一条语句中识别函数调用中的 defer 关键字,然后打印最后一条语句“third”。最后,main 函数 () 退出并检查是否有任何已 defer 的函数需要调用。由于有一个 defer 函数,所以继续调用该函数。
Golang 编程语言中 defer 关键字的关键优势在于资源清理,例如打开文件、处理数据库、网络连接等。在程序执行期间使用某些资源成功完成后,需要关闭每个使用的资源,以避免当其他程序等待同一资源时发生资源冲突。
Defer 语句通过关闭程序执行期间打开的所有文件或资源,使 Go 代码无错误。
在 Go 中,defer 通常用于资源清理。让我们通过一个写入文件的示例来理解。打开文件后需要关闭它。
package main
import (
"fmt"
"log"
"os"
)
func main() {
err := writeToFile("Some text")
if err != nil {
log.Fatalf(err.Error())
}
fmt.Printf("Write to file succesful")
}
func writeToFile(text string) error {
file, err := os.Open("temp.txt")
if err != nil {
return err
}
n, err := file.WriteString("Some text")
if err != nil {
return err
}
fmt.Printf("Number of bytes written: %d", n)
file.Close()
return nil
}
上面给出的 Go 代码打开一个文件。writeToFile 函数打开一个文件并向文件写入一些信息/内容。在完成写入文件后,下一步是关闭文件。写入操作可能会发生错误。在这种情况下,它将返回一个错误并退出函数。函数尝试关闭文件,使用的资源将被释放回系统。最后,在成功完成写入文件后返回一个 nil 值。
如果 file.WriteString 函数调用失败,函数将在不关闭文件的情况下返回,并将资源释放回系统。使用 defer 关键字而不是 file.Close() 语句可以修复此问题。defer 关键字在函数返回之前执行,以避免或修复此类问题。
重写上述代码后如下所示
package main
import (
"fmt"
"log"
"os"
)
func main() {
err := writeToFile("Some text")
if err != nil {
log.Fatalf(err.Error())
}
fmt.Printf("Write to file succesful")
}
func writeToFile(text string) error {
file, err := os.Open("temp.txt")
if err != nil {
return err
}
defer file.Close() //defer statement
n, err := file.WriteString("Some text")
if err != nil {
return err
}
fmt.Printf("Number of bytes written: %d", n)
return nil
}
defer file.Close() 在打开文件后关闭文件。这会推迟 file.close() 的执行,确保即使在写入同一文件时发生错误,文件也能被关闭。
一个 Golang 程序可以在程序中使用多个 defer 关键字。让我们通过下面的示例来检查,其中执行了三个 defer 语句。栈的概念在这里起作用。
这些被推入栈中。顶层持有最后打印的语句,即 third。

观察下面代码的输出,它按照推入栈的顺序反向打印。被 defer 的语句一个接一个地排列,导致 后进先出 的方式。
package main
import "fmt"
func main() {
defer fmt.Println("First")
defer fmt.Println("Second")
defer fmt.Println("Third")
}
输出
First Second Third
Defer 参数在 defer 语句被求值时进行求值。
让我们通过一个简单的程序来理解
package main
import "fmt"
func main() {
statement := "Go language in Learn eTurorials"
defer fmt.Printf("In defer statement is: %s\n", statement)
statement = "Learn eTurorials"
}
输出
In defer statement is: Go language in Learn eTurorials
在给定的程序中,一个字符串数据类型的变量 statement 被视为 defer 语句。defer 语句通过赋给 statement 变量的值 Go language in Learn eTutorials 进行求值。defer 语句打印变量 statement 的值。稍后在下一行更改 statement 变量的值为 “Learn eTutorials”。您可以看到上面代码的输出,它只评估 defer 语句变量,尽管赋给同一变量的值已经发生了变化。