原文鏈接:http://www.zhoubotong.site/post/50.html
defer陳述句用于延遲函式呼叫,每次會把一個函式壓入堆疊中,函式回傳前再把延遲的函式取出并執行,延遲函式可以有引數:
-
延遲函式的引數在defer陳述句出現時就已確定下來(傳值的就是當前值)
-
return先賦值(對于命名回傳值),然后執行defer,最后函式回傳
-
延遲函式執行按后進先出順序執行
-
延遲函式可操作主函式的變數名回傳值(修改回傳值)
-
defer后面的運算式可以是func或者是method的呼叫,如果defer的函式為nil,則會panic
日常開發中,使用不當很容易造成意外的“坑”,下面我整理了下常規使用場景下,defer的問題可能的踩坑匯總,
釋放資源
defer 陳述句正好是在函式退出時執行的陳述句,所以使用 defer 能非常方便地處理資源釋放、句柄關閉等問題,
package main
import (
"fmt"
"os"
)
func fileSize(filename string) int64 {
f, err := os.Open(filename)
if err != nil {
return 0
}
// 延遲呼叫Close, 此時Close不會被呼叫
defer f.Close()
info, err := f.Stat()
if err != nil {
// defer機制觸發, 呼叫Close關閉檔案
return 0
}
size := info.Size()
// defer機制觸發, 呼叫Close關閉檔案
return size
}
func main() {
fmt.Println(fileSize("demo.txt"))
}
變數捕獲
defer中的變數會被提前捕獲,后續的修改不會影響到已捕獲的值,舉個例子:
package main
import (
"fmt"
)
func main() {
i := 0
defer fmt.Println("Defer運行值:", i)
i = 10 // 這里雖然修改了值,但是不會影響上面的i值
fmt.Println("最后輸出值:", i)
}
結果defer陳述句中列印的值是修改前的值,:
最后輸出值: 10
Defer運行值: 0
變數名回傳值
在defer中修改具體變數名回傳值時,會影響到函式的實際回傳值,繼續舉個例子:
package main
import (
"fmt"
)
func ShowDefer() {
fmt.Println("最后輸出值:", deferValue())
}
func deferValue() (ret int) { // 注意這里回傳res變數值
ret = 0
defer func() { // 會直接修改堆疊中對應的回傳值
ret += 10
fmt.Println("Defer 運行值:", ret)
}()
ret = 2
fmt.Println("Ret重置值:", ret)
return //回傳的ret最后是 其實是本次2+上面的ret+=10的結果
}
func main() {
ShowDefer()
}
//Ret重置值: 2
//Defer 運行值: 12
//最后輸出值: 12
非變數名回傳值
當函式為非具體名回傳值時,defer無法影響回傳值(因在return時,對應回傳值已存入堆疊中),繼續舉個例子:
package main
import (
"fmt"
)
func ShowDefer() {
fmt.Println("最后輸出值:", deferValue())
}
func deferValue() int { // 非命名變數回傳
ret := 0
defer func() {
ret += 10
fmt.Println("Defer 運行值:", ret)
}()
ret = 2
return ret // 這里直接回傳ret2
}
func main() {
ShowDefer()
}
//Defer 運行值: 12
//最后輸出值: 2
經過上面的實踐理解,我們來看下下面的筆試題:
筆試題一
package main
import "fmt"
func f() (result int) {
defer func() {
result *= 7
}()
return 3
}
func main() {
fmt.Println(f())
}
問題決議:這里return先給result賦值為3,之后執行defer,result變為21,最后回傳21,
筆試題二
package main
import "fmt"
func f() int {
result := 3
defer func() {
result *= 7
}()
return result
}
func main() {
fmt.Println(f())
}
問題決議:這里return確定回傳值3,之后defer才修改result,最后函式回傳return確定的回傳值3,
筆試題三
package main
import "fmt"
// 多個defer
func multiDefer() {
for i := 3; i > 0; i-- {
defer func(n int) {
fmt.Print(n, " ")
}(i)
}
for i := 33; i > 30; i-- {
defer fmt.Print(i, " ")
}
}
func main() {
multiDefer()
}
問題決議:多個defer函式,按順序逆序執行,這里輸出31 32 33 1 2 3 ,
筆試題四
package main
import "fmt"
var fun func() string
func main() {
fmt.Println("hello monkey")
defer fun()
}
問題決議:由于這里的defer指定的func為nil,所以會panic ,
筆試題五
package main
import "fmt"
func main() {
for i := 3; i > 0; i-- {
defer func() {
fmt.Print(i, " ")
}()
}
}
問題決議:這里是極度容易踩坑的地方,由于defer這里呼叫的func沒有引數,等執行的時候,i已經為0(按3 2 1逆序,最后一個i=1時,i--的結果最后是0),所以這里輸出3個0 ,
如果還不太好理解?
package main
import "fmt"
func main() {
for i := 3; i > 1; i-- { // 回圈滿足條件的是 3 2,
defer func() { // 因為func 沒有引數,defer運行最后i--即 2-- 結果為 1
fmt.Print(i, " ") // 回圈2次 結果均為 1
}()
}
}//輸出 1 1
按照常規的思維理解應該是這樣:
package main
import "fmt"
func main() {
for i := 3; i > 0; i-- {
defer func(i int) {
fmt.Print(i, " ")
}(i)
}
}
感興趣的朋友可以細細品下,
無論從事什么行業,只要做好兩件事就夠了,一個是你的專業、一個是你的人品,專業決定了你的存在,人品決定了你的人脈,剩下的就是堅持,用善良專業和真誠贏取更多的信任,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/492479.html
標籤:其他
上一篇:Python雙人五子棋
下一篇:java多型詳解
