Golang defer关键字
最后修改于 2025 年 5 月 7 日
本教程解释了如何在Go中使用defer
关键字。我们将通过实际的资源清理和执行控制示例来涵盖defer基础知识。
defer
语句会将函数的执行推迟到周围函数返回为止。它通常用于清理操作,例如关闭文件或解锁互斥锁。
在Go中,defer
可以确保即使函数提前退出,重要的清理代码也会运行。当函数完成时,延迟的调用按后进先出(LIFO)的顺序执行。
基本defer示例
defer
最简单的用法是将函数调用推迟到周围函数完成之后。此示例演示了基本的defer行为。
package main import "fmt" func main() { defer fmt.Println("This prints last") fmt.Println("This prints first") fmt.Println("This prints second") }
延迟的调用在函数中所有非延迟语句之后执行。延迟的调用被推送到堆栈上,并按相反的顺序执行。
带有文件操作的Defer
defer
通常用于确保资源得到妥善关闭。此示例演示了使用defer进行文件清理。
package main import ( "fmt" "os" ) func main() { file, err := os.Create("test.txt") if err != nil { panic(err) } defer file.Close() fmt.Fprintln(file, "Writing to file") fmt.Println("File operations complete") }
file.Close()调用被推迟到main()退出为止。这确保了即使在文件操作过程中发生错误,文件也能被正确关闭。
多个defer语句
当存在多个defer语句时,它们会按相反的顺序执行。此示例演示了defer的LIFO(后进先出)行为。
package main import "fmt" func main() { defer fmt.Println("First defer - runs last") defer fmt.Println("Second defer - runs second") defer fmt.Println("Third defer - runs first") fmt.Println("Main function executing") }
延迟的调用被推送到堆栈上。当函数退出时,最后一个注册的defer是第一个执行的。这种行为对于正确的资源清理至关重要。
带有函数参数的Defer
延迟函数的参数会立即被评估,而不是在函数执行时被评估。此示例显示了参数评估的时间。
package main import "fmt" func main() { i := 0 defer fmt.Println("Deferred print:", i) i++ fmt.Println("Regular print:", i) }
延迟的fmt.Println在defer语句执行时捕获i的值(0),而不是在函数退出时。常规打印显示更新后的值(1)。
循环中的Defer
在循环中使用defer需要谨慎,因为延迟的调用直到函数退出才执行。此示例演示了循环defer的行为。
package main import "fmt" func main() { for i := 0; i < 5; i++ { defer fmt.Println("Deferred in loop:", i) fmt.Println("In loop:", i) } fmt.Println("Loop completed") }
循环中所有延迟的调用都会累积,并在循环完成后按相反的顺序执行。每次迭代的defer都会捕获i的当前值。
带有命名返回值的Defer
延迟的函数可以修改命名返回值。此示例显示了defer如何影响函数返回值。
package main import "fmt" func main() { fmt.Println("Result:", calculate()) } func calculate() (result int) { defer func() { result *= 2 }() result = 10 return }
延迟的函数在return语句之后,但在函数实际返回之前执行。它将命名返回值的执行结果加倍。
实际示例:数据库清理
这个实际示例演示了在真实场景中使用defer进行数据库连接清理。
package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" ) func main() { db, err := sql.Open("postgres", "user=test dbname=test sslmode=disable") if err != nil { log.Fatal(err) } defer db.Close() rows, err := db.Query("SELECT id, name FROM users") if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string if err := rows.Scan(&id, &name); err != nil { log.Fatal(err) } fmt.Printf("ID: %d, Name: %s\n", id, name) } if err := rows.Err(); err != nil { log.Fatal(err) } }
该示例显示了使用defer对数据库连接和查询结果进行正确的资源清理。这种模式可确保即使在处理过程中发生错误,资源也能被释放。
来源
本教程通过实际的资源管理和执行控制示例,涵盖了Go中的defer
关键字。