Golang中defer的三個實戰要點

咖啡色的羊駝發表於2018-09-09

前言

Golang中的defer是使用頻次比較高的,能創造出延遲生效特效的一種方式。

defer也有自己的矯情,需要注意的。 本文將從通過程式碼的方式來說明defer的三點矯情。

1.defer的生效順序 2.defer與return,函式返回值之間的順序 3.defer定義和執行兩個步驟,做的事情。

正文

1.defer的生效順序

先說結論:defer的執行順序是倒序執行(同入棧先進後出)

func main() {
	defer func() {
		fmt.Println("我後出來")
	}()
	defer func() {
		fmt.Println("我先出來")
	}()
}
複製程式碼

執行後列印出:

我先出來
我後出來
複製程式碼

2.defer與return,函式返回值之間的順序

先說結論:return最先執行->return負責將結果寫入返回值中->接著defer開始執行一些收尾工作->最後函式攜帶當前返回值退出

返回值的表達方式,我們知道根據是否提前宣告有兩種方式:一種是func test() int 另一種是 func test() (i int),所以兩種情況都來說說

func test() int

func main() {
	fmt.Println("main:", test())
}

func test() int {
	var i int
	defer func() {
		i++
		fmt.Println("defer2的值:", i)
	}()
	defer func() {
		i++
		fmt.Println("defer1的值:", i)
	}()
	return i
}
複製程式碼

輸出:

defer1的值: 1
defer2的值: 2
main: 0
複製程式碼

詳解:return的時候已經先將返回值給定義下來了,就是0,由於i是在函式內部宣告所以即使在defer中進行了++操作,也不會影響return的時候做的決定。

func test() (i int)

func main() {
	fmt.Println("main:", test())
}

func test() (i int) {
	defer func() {
		i++
		fmt.Println("defer2的值:", i)
	}()
	defer func() {
		i++
		fmt.Println("defer1的值:", i)
	}()
	return i
}
複製程式碼

輸出:

defer1的值: 1
defer2的值: 2
main: 2
複製程式碼

詳解:由於返回值提前宣告瞭,所以在return的時候決定的返回值還是0,但是後面兩個defer執行後進行了兩次++,將i的值變為2,待defer執行完後,函式將i值進行了返回。

3.defer定義和執行兩個步驟,做的事情

先說結論:會先將defer後函式的引數部分的值(或者地址)給先下來【你可以理解為()裡頭的會先確定】,後面函式執行完,才會執行defer後函式的{}中的邏輯

func test(i *int) int {
	return *i
}

func main(){
	var i = 1

	// defer定義的時候test(&i)的值就已經定了,是1,後面就不會變了
	defer fmt.Println("i1 ="  , test(&i))
	i++

	// defer定義的時候test(&i)的值就已經定了,是2,後面就不會變了
	defer fmt.Println("i2 ="  , test(&i))

	// defer定義的時候,i就已經確定了是一個指標型別,地址上的值變了,這裡跟著變
	defer func(i *int) {
		fmt.Println("i3 ="  , *i)
	}(&i)

	// defer定義的時候i的值就已經定了,是2,後面就不會變了
	defer func(i int) {
		//defer 在定義的時候就定了
		fmt.Println("i4 ="  , i)
	}(i)

	defer func() {
		// 地址,所以後續跟著變
		var c = &i
		fmt.Println("i5 ="  , *c)
	}()
	
	// 執行了 i=11 後才呼叫,此時i值已是11
	defer func() {
		fmt.Println("i6 ="  , i)
	}()

	i = 11
}
複製程式碼

相關文章