<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 錯(cuò)誤處理篇(二):defer 語(yǔ)句及其使用

          共 2867字,需瀏覽 6分鐘

           ·

          2021-03-14 13:28

          Go 語(yǔ)言中的類(lèi)沒(méi)有構(gòu)造函數(shù)和析構(gòu)函數(shù)的概念,處理錯(cuò)誤和異常時(shí)也沒(méi)有提供 try...catch...finally 之類(lèi)的語(yǔ)法,那當(dāng)我們想要在某個(gè)資源使用完畢后將其釋放(網(wǎng)絡(luò)連接、文件句柄等),或者在代碼運(yùn)行過(guò)程中拋出錯(cuò)誤時(shí)執(zhí)行一段兜底邏輯,要怎么做呢?

          通過(guò) defer 關(guān)鍵字聲明兜底執(zhí)行或者釋放資源的語(yǔ)句可以輕松解決這個(gè)問(wèn)題。比如我們看 Go 內(nèi)置的 io/ioutil 包提供的讀取文件方法 ReadFile 實(shí)現(xiàn)源碼,其中就有 defer 語(yǔ)句的使用:

          func ReadFile(filename string) ([]byte, error) {
              f, err := os.Open(filename)
              if err != nil {
                  return nil, err
              }
              defer f.Close()

              var n int64 = bytes.MinRead

              if fi, err := f.Stat(); err == nil {
                  if size := fi.Size() + bytes.MinRead; size > n {
                      n = size
                  }
              }
              return readAll(f, n)
          }

          defer 修飾的 f.Close() 方法會(huì)在函數(shù)執(zhí)行完成后或讀取文件過(guò)程中拋出錯(cuò)誤時(shí)執(zhí)行,以確保已經(jīng)打開(kāi)的文件資源被關(guān)閉,從而避免內(nèi)存泄露。如果一條語(yǔ)句干不完清理的工作,也可以在 defer 后加一個(gè)匿名函數(shù)來(lái)執(zhí)行對(duì)應(yīng)的兜底邏輯:

          defer func() { 
              //  執(zhí)行復(fù)雜的清理工作... 
          } ()

          另外,一個(gè)函數(shù)/方法中可以存在多個(gè) defer 語(yǔ)句,defer 語(yǔ)句的調(diào)用順序遵循先進(jìn)后出的原則,即最后一個(gè) defer 語(yǔ)句將最先被執(zhí)行,相當(dāng)于「?!惯@個(gè)數(shù)據(jù)結(jié)構(gòu),如果在循環(huán)語(yǔ)句中包含了 defer 語(yǔ)句,則對(duì)應(yīng)的 defer 語(yǔ)句執(zhí)行順序依然符合先進(jìn)后出的規(guī)則。

          由于 defer 語(yǔ)句的執(zhí)行時(shí)機(jī)和調(diào)用順序,所以我們要盡量在函數(shù)/方法的前面定義它們,以免在后面編寫(xiě)代碼時(shí)漏掉,尤其是運(yùn)行時(shí)拋出錯(cuò)誤會(huì)中斷后面代碼的執(zhí)行,也就感知不到后面的 defer 語(yǔ)句。

          下面我們看一段簡(jiǎn)單的 defer 示例代碼:

          package main

          import "fmt"

          func printError()  {
              fmt.Println("兜底執(zhí)行")
          }

          func main()  {
              defer printError()
              defer func() {
                  fmt.Println("除數(shù)不能是0!")
              }()

              var i = 1
              var j = 1
              var k = i / j

              fmt.Printf("%d / %d = %d\n", i, j, k)
          }

          在這段代碼中,我們定義了兩個(gè) defer 語(yǔ)句,并且是在函數(shù)最頂部,以確保異常情況下也能執(zhí)行。

          在函數(shù)正常執(zhí)行的情況下,這兩個(gè) defer 語(yǔ)句會(huì)在最后一條打印語(yǔ)句執(zhí)行完成后先執(zhí)行第二條 defer 語(yǔ)句,再執(zhí)行第一條 defer 語(yǔ)句:

          而如果我們把 j 的值設(shè)置為 0,則函數(shù)會(huì)拋出 panic:

          表示除數(shù)不能為零。這個(gè)時(shí)候,由于 defer 語(yǔ)句定義在拋出 panic 代碼的前面,所以依然會(huì)被執(zhí)行,底層的邏輯是在執(zhí)行 var k = i / j 這條語(yǔ)句時(shí),遇到除數(shù)為 0,則拋出 panic,然后立即中斷當(dāng)前函數(shù) main 的執(zhí)行(后續(xù)其他語(yǔ)句都不再執(zhí)行),并按照先進(jìn)后出順序依次執(zhí)行已經(jīng)在當(dāng)前函數(shù)中聲明過(guò)的 defer 語(yǔ)句,最后打印出 panic 日志及錯(cuò)誤信息。

          關(guān)于 panic 及其內(nèi)部執(zhí)行邏輯,學(xué)院君將在下一篇教程給大家介紹。

          總結(jié)一下,Go 語(yǔ)言的 defer 語(yǔ)句相當(dāng)于 Java/PHP 中的析構(gòu)函數(shù)和 finally 語(yǔ)句的功效,常用于定義兜底邏輯,在函數(shù)執(zhí)行完畢后或者運(yùn)行拋出 panic 時(shí)執(zhí)行,如果一個(gè)函數(shù)定義了多個(gè) defer 語(yǔ)句,則按照先進(jìn)后出的順序執(zhí)行。

          (本文完)


          學(xué)習(xí)過(guò)程中有任何問(wèn)題,可以通過(guò)下面的評(píng)論功能或加入「Go 語(yǔ)言研習(xí)社」與學(xué)院君討論:


          本系列教程首發(fā)在 geekr.dev,你可以點(diǎn)擊頁(yè)面左下角閱讀原文鏈接查看最新更新的教程。

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.亚洲黄色 | 黄色大片视频 | 午夜性福利视频 | 台湾 无码 | 操逼视频免费无码 |