Go中的defer看似很簡單,實則一點都不難

Golang 中的 Defer

在Go語言中,defer語句用于將一個函數調用推遲到外圍函數返回之后執行。它常用于確保某些操作在函數結束時一定會執行,例如資源釋放、文件關閉等。

基本語法

defer語句的基本使用方法如下:

func main() {defer fmt.Println("World")fmt.Println("Hello")
}

輸出:

Hello
World

在上面的例子中,fmt.Println("World")defer語句中,所以它會在main函數結束時執行,而不是在定義它的地方立即執行。

多個defer

如果在一個函數中有多個defer語句,它們的執行順序是后進先出(LIFO)的。也就是說,最后一個defer語句會最先執行。

func main() {defer fmt.Println("First")defer fmt.Println("Second")defer fmt.Println("Third")fmt.Println("Hello")
}

輸出:

Hello
Third
Second
First

典型用例

1. 文件操作

在處理文件操作時,可以使用defer確保文件關閉:

package mainimport ("fmt""os"
)func main() {file, err := os.Open("example.txt")if err != nil {fmt.Println(err)return}defer file.Close()// 讀取文件內容
}

2. 資源釋放

defer還可以用于釋放其它類型的資源,例如網絡連接、數據庫連接等:

package mainimport ("database/sql"_ "github.com/go-sql-driver/mysql""log"
)func main() {db, err := sql.Open("mysql", "user:password@/dbname")if err != nil {log.Fatal(err)}defer db.Close()// 數據庫操作
}

3. 鎖的解鎖

在并發編程中,可以使用defer確保鎖在臨界區操作完成后被釋放:

package mainimport ("fmt""sync"
)var mu sync.Mutexfunc main() {mu.Lock()defer mu.Unlock()// 臨界區操作fmt.Println("Critical section")
}

defer與匿名函數

有時候,我們需要在defer中執行更復雜的操作,此時可以使用匿名函數:

package mainimport "fmt"func main() {defer func() {fmt.Println("Deferred call")}()fmt.Println("Main function")
}

輸出:

Main function
Deferred call

defer與返回值

defer還可以用來修改返回值:

package mainimport "fmt"func test() (result int) {defer func() {result++}()return 0
}func main() {fmt.Println(test())  // 輸出1
}

在上面的例子中,defer中的匿名函數會在test函數返回之前執行,并修改result的值。

defer 誤區

以下,我將以對比的形式展開對 defer 誤區的展示。(你可以先自己猜一下每段代碼的執行結果)

誤區一

func Defer() {i := 0defer func() {println(i)}()i = 1
}

在這個函數中,defer 語句延遲執行匿名函數,直到 Defer 函數即將返回。

這個匿名函數是一個閉包,它捕獲并引用了外部變量 i

因此,當 defer 延遲的匿名函數最終執行時,它打印的是閉包捕獲的變量 i 的當前值。

  1. 定義變量 i 并賦值為 0
  2. 定義 defer 延遲執行的匿名函數,它捕獲變量 i
  3. 將變量 i 修改為 1
  4. Defer 函數即將返回時,執行 defer 延遲的匿名函數,打印捕獲的變量 i 的當前值 1
    因此,Defer 函數的輸出是 1
func DeferV1() {i := 0// 立即調用 defer 延遲的匿名函數,并將當前變量 i 的值傳遞給它的參數 i。defer func(i int) {println(i)}(i)i = 1
}

在這個函數中,defer 語句延遲執行的匿名函數有一個參數 i,并且在調用時將當前的 i 值作為參數傳遞給匿名函數。

這意味著在 defer 聲明時,匿名函數參數 i 的值已經確定,并且與外部變量 i 無關。

  1. 定義變量 i 并賦值為 0
  2. 定義 defer 延遲執行的匿名函數,并立即將當前變量 i(值為 0)傳遞給它的參數 i
  3. 將外部變量 i 修改為 1
  4. DeferV1 函數即將返回時,執行 defer 延遲的匿名函數,打印參數 i 的值 0

因此,DeferV1 函數的輸出是 0

誤區二

func DeferReturn() int {a := 0defer func() {a = 1}()return a
}

在這個函數中,變量 a 是一個局部變量。

return a 語句執行時,defer 語句的執行會在 return 語句之后立即發生,但在實際返回值被傳遞給調用者之前。

  1. 定義變量 a 并賦值為 0
  2. 設置 defer 延遲執行的匿名函數,將變量 a 修改為 1
  3. 執行 return a,此時返回值為 0
  4. defer 延遲的匿名函數執行,將變量 a 修改為 1,但此時返回值已經確定為 0

因此,DeferReturn 函數的返回值是 0

func DeferReturnV1() (a int) {// 全局可見 命名返回值 aa = 0defer func() {a = 1}()return
}

在這個函數中,變量 a 是一個命名返回值。

在 Go 語言中,當一個函數聲明了命名返回值時,該返回值變量會在函數開始時被隱式聲明,并且在整個函數體中都是可見的。

因此,當 return 語句執行時,返回的值是命名返回值變量 a 的當前值。

  1. 定義命名返回值變量 a 并隱式初始化為 0
  2. 設置 defer 延遲執行的匿名函數,將變量 a 修改為 1
  3. 執行 return 語句,返回命名返回值變量 a
  4. 在實際返回值被傳遞給調用者之前,執行 defer 延遲的匿名函數,將變量 a 修改為 1

因此,DeferReturnV1 函數的返回值是 1

誤區三

type MyStruct struct {name string
}func DeferReturnV2() *MyStruct {a := &MyStruct{name: "ypb",}defer func() {a.name = "zmz"}()return a
}

關鍵點

  1. 指針修改

a 是一個指向 MyStruct 實例的指針。

defer 延遲的匿名函數中,修改的是 a 指向的對象的 name 字段,而不是指針本身。

這意味著,即使 return a 語句執行時,defer 語句會在實際返回之前執行,修改指針所指向的對象的內容。

  1. defer 的執行順序

defer 語句會在包含它的函數返回之前執行。

具體來說,defer 延遲的匿名函數在 return 語句設置返回值之后,實際返回之前執行。

因此,匿名函數在返回之前對指針所指向的對象的修改是有效的。

本例子執行順序:

  1. 創建 MyStruct 實例并賦值給 a,此時 a.name = "ypb"
  2. 設置 defer 延遲執行的匿名函數,準備在函數返回之前執行。
  3. 執行 return a,此時準備返回 a 指向的對象。
  4. 在返回之前,執行 defer 延遲的匿名函數,將 a.name 修改為 "zmz"
  5. 返回 a,此時 a 指向的 MyStruct 實例的 name 字段已經被修改為 "zmz"

defer 自測

只需要自己猜測一下代碼的輸出結果即可。

很多人誤以為在循環中使用 defer 會在每次迭代時執行推遲的操作。實際上,defer 是在函數返回時才執行的,因此在循環中多次使用 defer 會在函數結束時按照后進先出順序依次執行所有的 defer 語句。

測試一:

// DeferClosureLoop1 函數的輸出結果為 十個 10
func DeferClosureLoop1() {for i := 0; i < 10; i++ {i := idefer func() {println(i)}()}
}

測試二:

// DeferClosureLoop2 函數的輸出結果為 9 ~ 0
func DeferClosureLoop2() {for i := 0; i < 10; i++ {defer func(val int) {println(val)}(i)}
}

測試三:

// DeferClosureLoop3 與 DeferClosureLoop2 函數的輸出結果相同
func DeferClosureLoop3() {for i := 0; i < 10; i++ {j := idefer func() {println(j)}()}
}

總結

defer在 Go 語言中用于推遲函數調用,直到外圍函數返回。

常見用法包括確保資源釋放(如文件關閉、解鎖)、在多層嵌套函數中統一處理異常等。

defer語句按后進先出順序執行,支持匿名函數并可修改命名返回值。

需注意變量捕獲、循環中使用defer導致堆積等誤區。正確使用defer有助于代碼清晰與資源管理。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45531.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45531.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45531.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

距離變換 Distance Transformation

以下為該學習地址的學習筆記&#xff1a;Distance transformation in image - Python OpenCV - GeeksforGeeks 其他學習資料&#xff1a;Morphology - Distance Transform 簡介 距離變換是一種用于計算圖像中每個像素與最近的非零像素之間距離的技術。它通常用于圖像分割和物體…

51單片機5(GPIO簡介)

一、序言&#xff1a;不論學習什么單片機&#xff0c;最簡單的外設莫過于I口的高低電平的操作&#xff0c;接下來&#xff0c;我們將給大家介紹一下如何在創建好的工程模板上面&#xff0c;通過控制51單片機的GPIO來使我們的開發板上的LED來點亮。 二、51單片機GPIO介紹&#…

第三節SHELL腳本中的變量與運算(1.1-1.5)

一,腳本中的變量 1,1什么是變量 在編寫程序是,通常會遇到被操作對象不固定的情況我們需要用一串固定的字符來的表示不固定的值,這就是變量存在的根本意義變量的實現原理就是內存存儲單元的一個符合名稱 1,2 變量的命名規則 變量的名稱中只能包含數字,大小寫字母以及下劃線 …

PySide在Qt Designer中使用QTableView 顯示表格數據

在 PySide6 中&#xff0c;可以使用 Qt Model View 架構中的 QTableView 部件來顯示和編輯表格數據。 1、創建ui文件 在Qt Designer中新建QMainWindow&#xff0c;命名為csvShow.ui。QMainWindow上有兩個部件&#xff1a;tableview和btn_exit。 2、使用pyuic工具將ui文件轉換為…

Kafka(四) Consumer消費者

一&#xff0c;基礎知識 1&#xff0c;消費者與消費組 每個消費者都有對應的消費組&#xff0c;不同消費組之間互不影響。 Partition的消息只能被一個消費組中的一個消費者所消費&#xff0c; 但Partition也可能被再平衡分配給新的消費者。 一個Topic的不同Partition會根據分配…

MySQL集群、Redis集群、RabbitMQ集群

一、MySQL集群 1、集群原理 MySQL-MMM 是 Master-Master Replication Manager for MySQL&#xff08;mysql 主主復制管理器&#xff09;的簡稱。腳本&#xff09;。MMM 基于 MySQL Replication 做的擴展架構&#xff0c;主要用來監控 mysql 主主復制并做失敗轉移。其原理是將真…

環境變量在Gradle中的妙用:構建自動化的秘訣

環境變量在Gradle中的妙用&#xff1a;構建自動化的秘訣 在構建自動化的過程中&#xff0c;環境變量扮演著至關重要的角色。它們允許開發者根據不同的運行環境&#xff08;如開發、測試和生產環境&#xff09;來調整配置&#xff0c;而無需修改代碼。Gradle&#xff0c;作為一…

基于Faster R-CNN的安全帽目標檢測

基于Faster R-CNN的安全帽目標檢測項目通常旨在解決工作場所&#xff0c;特別是建筑工地的安全監管問題。這類項目使用計算機視覺技術&#xff0c;特別是深度學習中的Faster R-CNN算法&#xff0c;來自動檢測工人是否正確佩戴了安全帽&#xff0c;從而確保遵守安全規定并減少事…

實驗一:圖像信號的數字化

目錄 一、實驗目的 二、實驗原理 三、實驗內容 四、源程序及結果 源程序&#xff08;python&#xff09;&#xff1a; 結果&#xff1a; 五、結果分析 一、實驗目的 通過本實驗了解圖像的數字化過程&#xff0c;了解數字圖像的數據矩陣表示法。掌握取樣&#xff08;象素個…

用Python爬蟲能實現什么?得到什么?

Python爬蟲是一種強大的工具&#xff0c;可以用來自動化地從互聯網上抓取數據和信息。使用Python實現爬蟲可以達成多種目的&#xff0c;包括但不限于以下幾個方面&#xff1a; 數據收集&#xff1a; 網頁內容抓取&#xff1a;可以抓取網頁上的文本、圖片、視頻等內容。搜索引擎…

Linux 網絡配置與連接

一、網絡配置 1.1 ifconfig 網卡配置查詢 ifconfig #查看所有啟動的網絡接口信息 ifconfig 指定的網卡 #查看指定網絡接口信息 1.2 修改網絡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 #ens33網絡配置文…

【電源拓撲】反激拓撲

目錄 工作模式 固定頻率 CCM連續電流模式 DCM不連續電流模式 可變頻率 CRM電流臨界模式 反激電源CRM工作模式為什么要跳頻 反激電源應用場景 為什么反激電源功率做不大 電感電流爬升 反激變壓器的限制條件 精通反激電源設計的關鍵-反激電源變壓器設計 反激電源變壓…

MySQL 事務與鎖

事務ACID特性 原子性&#xff1a;事務要么同時成功&#xff0c;要么同時失敗&#xff0c;事務的原子性通過undo log日志保證 一致性&#xff1a;業務代碼要拋出報錯&#xff0c;讓數據庫回滾 隔離性&#xff1a;事務并發執行時&#xff0c;他們內部操作不能互相干擾 持久性&…

Python 讀取esxi上所有主機的設備信息

&#xff08;主要是為了統計所有虛擬機的設備名稱和所屬主機&#xff09; 代碼&#xff1a; from pyVim import connect from pyVmomi import vim import ssldef get_vm_devices(vm):devices []try:if vm.config is not None and hasattr(vm.config, hardware) and hasattr(v…

SpringBoot解決Apache Tomcat輸入驗證錯誤漏洞

Apache Tomcat是美國阿帕奇&#xff08;Apache&#xff09;基金會的一款輕量級Web應用服務器。該程序實現了對Servlet和JavaServer Page&#xff08;JSP&#xff09;的支持。 Apache Tomcat存在輸入驗證錯誤漏洞&#xff0c;該漏洞源于HTTP/2請求的輸入驗證不正確&#xff0c;會…

postgresql簡單導出數據與手動本地恢復(小型數據庫)

問題 需要每天手動備份postgresql。 步驟 導出數據 /opt/homebrew/opt/postgresql16/bin/pg_dump --file/Users/zhangyalin/backup_sql/<IP地址>_pg-2024_07_15_17_30_15-dump.sql --dbname<數據庫名> --username<用戶名> --host<IP地址> --port54…

Day53:圖論 島嶼數量 島嶼的最大面積

99. 島嶼數量 時間限制&#xff1a;1.000S 空間限制&#xff1a;256MB 題目描述 給定一個由 1&#xff08;陸地&#xff09;和 0&#xff08;水&#xff09;組成的矩陣&#xff0c;你需要計算島嶼的數量。島嶼由水平方向或垂直方向上相鄰的陸地連接而成&#xff0c;并且四周…

低空經濟持續發熱,無人機培訓考證就業市場及前景剖析

隨著科技的不斷進步和社會需求的日益增長&#xff0c;低空經濟已成為全球及我國經濟增長的新引擎。作為低空經濟的重要組成部分&#xff0c;無人機技術因其廣泛的應用領域和顯著的經濟效益&#xff0c;受到了社會各界的廣泛關注。為滿足市場對無人機人才的需求&#xff0c;無人…

深入剖析 Android 開源庫 EventBus 的源碼詳解

文章目錄 前言一、EventBus 簡介EventBus 三要素EventBus 線程模型 二、EventBus 使用1.添加依賴2.EventBus 基本使用2.1 定義事件類2.2 注冊 EventBus2.3 EventBus 發起通知 三、EventBus 源碼詳解1.Subscribe 注解2.注冊事件訂閱方法2.1 EventBus 實例2.2 EventBus 注冊2.2.1…

夢想CAD在線預覽編輯功能

1.最近有個需求&#xff0c;在web系統里進行在線進行CAD預覽和編輯&#xff0c;這里用的是夢想CAD實現此功能&#xff0c;夢想CAD官網文檔 2.CAD預覽&#xff0c;需要需要對CAD文件格式進行轉化&#xff0c;將dwg文件格式轉化為mxweb格式&#xff0c;再進行調用夢想CAD里的打開…