首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
函数参数与返回值
传递变长参数
defer 和追踪
内置函数
递归函数
将函数作为参数
闭包
应用闭包:将函数作为返回值
使用闭包调试
计算函数执行时间
通过内存缓存来提升性能
声明和初始化
切片
For-range 结构
切片重组(reslice)
切片的复制与追加
字符串、数组和切片的应用
声明、初始化和 make
测试键值对是否存在及删除元素
for-range 的配套用法
map 类型的切片
map 的排序
将 map 的键值对调
标准库概述
regexp 包
锁和 sync 包
精密计算和 big 包
自定义包和可见性
为自定义包使用 godoc
使用 go install 安装自定义包
自定义包的目录结构、go install 和 go test
通过 Git 打包和安装
Go 的外部包和项目
在 Go 程序中使用外部库
结构体定义
使用工厂方法创建结构体实例
使用自定义包中的结构体
带标签的结构体
匿名字段和内嵌结构体
方法
类型的 String() 方法和格式化描述符
垃圾回收和 SetFinalizer
当前位置:
首页>>
技术小册>>
go编程权威指南(二)
小册名称:go编程权威指南(二)
关键字 `defer` 允许我们推迟到函数返回之前(或任意位置执行 `return` 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 `return` 语句同样可以包含一些操作,而不是单纯地返回某个值)。 关键字 `defer` 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块,它一般用于释放某些已分配的资源。 示例 6.8 [defer.go](examples/chapter_6/defer.go): ```go package main import "fmt" func main() { function1() } func function1() { fmt.Printf("In function1 at the top\n") defer function2() fmt.Printf("In function1 at the bottom!\n") } func function2() { fmt.Printf("Function2: Deferred until the end of the calling function!") } ``` 输出: ``` In Function1 at the top In Function1 at the bottom! Function2: Deferred until the end of the calling function! ``` 请将 `defer` 关键字去掉并对比输出结果。 使用 `defer` 的语句同样可以接受参数,下面这个例子就会在执行 `defer` 语句时打印 `0`: ```go func a() { i := 0 defer fmt.Println(i) i++ return } ``` 当有多个 `defer` 行为被注册时,它们会以逆序执行(类似栈,即后进先出): ```go func f() { for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) } } ``` 上面的代码将会输出:`4 3 2 1 0`。 关键字 `defer` 允许我们进行一些函数执行完成后的收尾工作,例如: 1. 关闭文件流 (详见 [第 12.2 节](12.2.md)) ```go // open a file defer file.Close() ``` 2. 解锁一个加锁的资源 (详见 [第 9.3 节](09.3.md)) ```go mu.Lock() defer mu.Unlock() ``` 3. 打印最终报告 ```go printHeader() defer printFooter() ``` 4. 关闭数据库链接 ```go // open a database connection defer disconnectFromDB() ``` 合理使用 `defer` 语句能够使得代码更加简洁。 以下代码模拟了上面描述的第 4 种情况: ```go package main import "fmt" func main() { doDBOperations() } func connectToDB() { fmt.Println("ok, connected to db") } func disconnectFromDB() { fmt.Println("ok, disconnected from db") } func doDBOperations() { connectToDB() fmt.Println("Defering the database disconnect.") defer disconnectFromDB() //function called here with defer fmt.Println("Doing some DB operations ...") fmt.Println("Oops! some crash or network error ...") fmt.Println("Returning from function here!") return //terminate the program // deferred function executed here just before actually returning, even if // there is a return or abnormal termination before } ``` 输出: ``` ok, connected to db Defering the database disconnect. Doing some DB operations ... Oops! some crash or network error ... Returning from function here! ok, disconnected from db ``` **使用 `defer` 语句实现代码追踪** 一个基础但十分实用的实现代码执行追踪的方案就是在进入和离开某个函数打印相关的消息,即可以提炼为下面两个函数: ```go func trace(s string) { fmt.Println("entering:", s) } func untrace(s string) { fmt.Println("leaving:", s) } ``` 以下代码展示了何时调用这两个函数: 示例 6.10 [defer_tracing.go](examples/chapter_6/defer_tracing.go): ```go package main import "fmt" func trace(s string) { fmt.Println("entering:", s) } func untrace(s string) { fmt.Println("leaving:", s) } func a() { trace("a") defer untrace("a") fmt.Println("in a") } func b() { trace("b") defer untrace("b") fmt.Println("in b") a() } func main() { b() } ``` 输出: ``` entering: b in b entering: a in a leaving: a leaving: b ``` 上面的代码还可以修改为更加简便的版本(示例 6.11 [defer_tracing2.go](examples/chapter_6/defer_tracing2.go)): ```go package main import "fmt" func trace(s string) string { fmt.Println("entering:", s) return s } func un(s string) { fmt.Println("leaving:", s) } func a() { defer un(trace("a")) fmt.Println("in a") } func b() { defer un(trace("b")) fmt.Println("in b") a() } func main() { b() } ``` **使用 `defer` 语句来记录函数的参数与返回值** 下面的代码展示了另一种在调试时使用 `defer` 语句的手法(示例 6.12 [defer_logvalues.go](examples/chapter_6/defer_logvalues.go)): ```go package main import ( "io" "log" ) func func1(s string) (n int, err error) { defer func() { log.Printf("func1(%q) = %d, %v", s, n, err) }() return 7, io.EOF } func main() { func1("Go") } ``` 输出: Output: 2011/10/04 10:46:11 func1("Go") = 7, EOF
上一篇:
传递变长参数
下一篇:
内置函数
该分类下的相关小册推荐:
企业级Go应用开发从零开始
Go开发基础入门
Go Web编程(下)
深入浅出Go语言核心编程(四)
WebRTC音视频开发实战
深入浅出Go语言核心编程(二)
GO面试指南
Go 组件设计与实现
Go语言从入门到实战
深入浅出Go语言核心编程(三)
Golang修炼指南
Go语言入门实战经典