Golang, разберитесь с использованием defer в 9 коротких фрагментах кода

Go Язык программирования
Golang, разберитесь с использованием defer в 9 коротких фрагментах кода

Автор: Линь Гуаньхун / Призраки под рукой

Самородки:Талант /user/178526…

Блог:www.cnblogs.com/linguanh/

Гитхаб:GitHub.com/afan913337456…

Облачная колонка Tencent:cloud.Tencent.com/developer/U…

Колонка блокчейна червоточины:woohoo.impulsecommunity.com/article/153…


пролог

deferдаGoОчень важное ключевое слово в языке. Эта статья в основном посвящена简短的手法Перечислите различные эффекты, которые он показывает в различных видах общих фрагментов кода. С точки зрения письменного теста можно сказать, что он охватывает большинство типов вопросов.

Кроме того, перед этой статьей есть еще одна моя статья, в которой используется тот же例子的形式правильноchannelтип данных делать直观讲解статья.

Golang, поймите основы каналов в 17 коротких фрагментах кода

содержание

  • Основные характеристики отсрочки
  • передача по ссылке наdeferВызванная функция, и это случай незамкнутой функции
  • передать ссылку наdeferВызывается функция, даже если функция закрытия не используется
  • передать значение вdeferВызываемая функция и незамкнутая функция
  • deferКогда вызывается замыкающая функция, а внутри вызывается внешняя непараметрическая переменная
  • deferВызвать функцию закрытия, если значение переданного параметра используется внутри
  • deferВызываемая незамыкающая функция, если параметр является функцией
  • deferне влияетreturnзначение
  • Пара замыкающих функцийdeferВлияние

Основные характеристики отсрочки

  • отложенный вызов
  • в той функции, где он находитсяreturnилиpanicили执行完毕называется после
  • Множественные отсрочки в порядке их вызоваформа. Первый пришел, последний вышел, сначала определен, а затем вызван
func Test_1(t *testing.T) {
	// defer 的调用顺序。由下到上,为 栈的形式。先进后出
	defer0()   // ↑
	defer1()   // |
	defer2()   // |
	defer3()   // |
	//defer4() // |
	defer5()   // |
	defer6()   // |  
	defer7()   // |
	defer8()   // |  从下往上
}

передача по ссылке наdeferФункция для вызова, и это не замыкающая функция, значение不会затронуты последующими изменениями

func defer0() {
	a := 3  // a 作为演示的参数
	defer fmt.Println(a) // 非引用传参,非闭包函数中,a 的值 不会 受后面的改变影响
	a = a + 2
}
// 控制台输出 3

передать ссылку наdeferВызванная функция, даже если функция закрытия не используется, значение равнозатронуты последующими изменениями

func myPrintln(point *int)  {
	fmt.Println(*point) // 输出引用所指向的值
}
func defer1() {
	a := 3
	// &a 是 a 的引用。内存中的形式: 0x .... ---> 3
	defer myPrintln(&a) // 传递引用给函数,即使不使用闭包函数,值 会 受后面的改变影响
	a = a + 2
}
// 控制台输出 5

передать значение вdeferВызываемая функция и незамыкающая функция, значение不会затронуты последующими изменениями

func p(a int)  {
	fmt.Println(a)
}

func defer2() {
	a := 3
	defer p(a) // 传递值给函数,且非闭包函数,值 不会 受后面的改变影响
	a = a + 2
}
// 控制台输出: 3

deferВызовите функцию закрытия и вызовите внешнюю непараметрическую переменную, значениезатронуты последующими изменениями

// 闭包函数内,事实是该值的引用
func defer3() {
	a := 3
	defer func() {
		fmt.Println(a) // 闭包函数内调用外部非传参进来的变量,事实是该值的引用,值 会 受后面的改变影响
	}()
	a = a + 2  // 3 + 2 = 5
}
// 控制台输出: 5
// defer4 会抛出数组越界错误。
func defer4() {
	a := []int{1,2,3}
	for i:=0;i<len(a);i++ {
		// 同 defer3 的闭包形式。因为 i 是外部变量,没用通过传参的形式调用。在闭包内,是引用。
		// 值 会 受 ++ 改变影响。导致最终 i 是3, a[3] 越界
		defer func() {
			fmt.Println(a[i])
		}()
	}
}
// 结果:数组越界错误

deferВызвать функцию закрытия, если значение переданного параметра используется внутри. значение используется

func defer5() {
	a := []int{1,2,3}
	for i:=0;i<len(a);i++ {
		// 闭包函数内部使用传参参数的值。内部的值为传参的值。同时引用是不同的
		defer func(index int) {
		        // index 有一个新地址指向它
			fmt.Println(a[index]) // index == i
		}(i)
		// 后进先出,3 2 1
	}
}
// 控制台输出: 
//     3
//     2
//     1

deferВызываемая незамыкающая функция, если параметр является функцией, то она будет выполняться первой по порядку (параметры функции)

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}
func defer6()  {
	a := 1
	b := 2
	// calc 充当了函数中的函数参数。即使在 defer 的函数中,它作为函数参数,定义的时候也会首先调用函数进行求值
	// 按照正常的顺序,calc("10", a, b) 首先被调用求值。calc("122", a, b) 排第二被调用
	defer calc("1", a, calc("10", a, b))
	defer calc("12",a, calc("122", a, b))
}
// 控制台输出:
/**
10 1 2 3   // 第一个函数参数
122 1 2 3  // 第二个函数参数
12 1 3 4   // 倒数第一个 calc
1 1 3 4    // 倒数第二个 calc
*/

deferне влияетreturnзначение

Вывод из следующих двух примеров таков:

  • Неважно, передает ли внутренний вызов defer значение или ссылку. Ни один из них не изменит возвращаемый результат return. Определение возвращаемого значения до отсрочки
func defer7() int {
	a := 2
	defer func() {
		a = a + 2
	}()
	return a
}
// 控制台输出:2
func add(i *int)  {
	*i = *i + 2
}

func defer8() int {
	a := 2
	defer add(&a)
	return a
}
// 控制台输出:2

принцип:

    例如:return a,此行代码经过编译后,会被拆分为:
    1. 返回值 = a
    2. 调用 defer 函数
    3. return

Пара замыкающих функцийdeferВлияние

в функции,值传递и引用传递Различие между ними относительно простое, основанное на знании указателей в языке C.

Что касается того, почему функция закрытия изменена defer, если функция не использует переданные параметры, она может играть роль модификации ссылки. Принцип заключается в следующем:

a := 2
func() {
    fmt.Println(a)
}()
a = a + 3
// 内存
闭包外:
    1. a 实例化
    2. a地址 ---> 2
闭包内:
    1. a 地址被传递进来
    2. a地址 ---> 2
    3. a = a + 3
    4. 输出 5

над