go语言的defer

发布时间 2023-06-01 10:00:44作者: 念秋

go语言的defer机制可以避免其他语言时处理错误,要在每个分支执行关闭、回收资源的繁杂问题。

百闻不如一见,看的教程再多,也不如自己实际编程,调试来得方便。

以下为根据测试代码段进行总结的过程。

1.

package main

import "fmt"

func test1() {
	fmt.Println("循环开始")
	var p *int
	for i := 0; i < 5; i++ {
		fmt.Println("i = ", i)
		defer func(i int) {
			fmt.Println("this is first defer ", i)
		}(i) //带参数
		defer func() {
			fmt.Println("this is second defer", i*10)
		}() //不带参数
		p = &i
	}
	//return	//在此处注释
	fmt.Println("循环结束")
	defer func() {
		fmt.Println("this is the last defer")
	}()
	fmt.Println("修改循环变量i指针之前 i = ", *p)
	*p = 1	//在此处注释
	fmt.Println("修改循环变量i指针之后 i = ", *p)
}
func main() {
	test1()
}

循环开始
i =  0                      
i =  1                      
i =  2                      
i =  3                      
i =  4                      
循环结束                    
修改循环变量i指针之前 i =  5
修改循环变量i指针之后 i =  1
this is the last defer      
this is second defer 10     
this is first defer  4      
this is second defer 10     
this is first defer  3      
this is second defer 10     
this is first defer  2
this is second defer 10
this is first defer  1
this is second defer 10
this is first defer  0

解释:test1()函数有两处代码注释,将其注释或者打开,可以得出以下结论。

1.defer要执行的函数或者语句,与defer语句出现的先后顺序有关系。假设你把每一个defer语句看作 stack.push(),就很容易理解defer执行的顺序了。显然,如果打开return处的注释,就不会执行return后面的defer了。

2.defer函数和普通函数一样,可以带参数,需考虑参数是值还是指针。这一点观看循环里面的输出就很好理解了。不带参数,就有对循环变量i的引用。

func test2() {
	f, _ := os.Open(`C:\Users\yyjeqhc\Desktop\bc\1.cpp`)
	defer func() {
		if err := f.Close(); err != nil {
			fmt.Println("1.cpp已经关闭")
		} else {
			fmt.Println("1.cpp在这里关闭")
		}
	}()
	var fa = func(f *os.File) {
		if err := f.Close(); err != nil {
			fmt.Println("文件已经关闭")
		} else {
			name := f.Name()
			filename := path.Base(name)
			//fmt.Println("不包含目录的文件名:", filename)
			fmt.Println("关闭了", filename)
		}
	}
	defer fa(f)
	f1, _ := os.Open(`C:\Users\yyjeqhc\Desktop\bc\1.py`)
	defer func() {
		if err := f1.Close(); err != nil {
			fmt.Println("1.py已经关闭")
		} else {
			fmt.Println("1.py在这里关闭")
		}
	}()
	defer fa(f1)
	var t = func(i int) {
		fmt.Println("this is a func and i = ", i)
		defer func() {
			fmt.Println("this is defer in a func", i)
		}()
	}
	t(1)
	fmt.Println("-----------------")
	t(2)
	fmt.Println("this is end")
}

可见输出

this is a func and i =  1
this is defer in a func 1               
-----------------                       
this is a func and i =  2               
this is defer in a func 2               
this is end                             
关闭了 C:\Users\yyjeqhc\Desktop\bc\1.py 
1.py已经关闭                            
关闭了 C:\Users\yyjeqhc\Desktop\bc\1.cpp
1.cpp已经关闭

得出结论:就算是匿名函数里面的defer,也会在匿名函数调用结束后执行。也就是defer以函数分层,每层函数都有自己的defer调用栈。

欢迎提出有趣的代码片段以加深理解。