襄阳作风建设年网站,wordpress段落缩进,物联网开发平台,怎让做淘宝网站defer概览 
defer是go语言里的一个关键字,在 函数内部使用;defer关键字后面跟一个 函数或匿名函数; 
defer用法 
执行一些资源的收尾工作,如 关闭数据库连接,关闭文件描述符,释放资源等等;结合recover()函数使用,防止函数内部的异常导致整个程序停止;defer在遇到panic后,仍然会…defer概览 
defer是go语言里的一个关键字,在 函数内部使用;defer关键字后面跟一个 函数或匿名函数; 
defer用法 
执行一些资源的收尾工作,如 关闭数据库连接,关闭文件描述符,释放资源等等;结合recover()函数使用,防止函数内部的异常导致整个程序停止;defer在遇到panic后,仍然会执行(defer语句要在panic之前编译),从而保证即使出错也能进行对应的错误处理; 
函数中多个defer间存储方式及运行顺序 
defer数据结构 
type _defer struct {siz       int32started   boolopenDefer boolsp        uintptrpc        uintptrfn        *funcval_panic    *_paniclink      *_defer
}_defer结构体中的link字段 指向下一个defer结构体地址的指针;_defer结构体是 延迟调用链表中的一个元素,所有的结构体都是通过 link字段串联成链表;使用链表方式存储, 代码从上到下编译,遇到的 defer都会放到链表头部,后面执行的执行按照链表顺序从头到尾执行defer编译及执行顺序 就是 栈的入栈出栈的顺序 
defer必须要了解的特性 
多defer执行顺序 
结论 
多defer执行顺序是 最后编译的先执行; 
defer后面的函数的参数值的预计算 
结论 
defer在编译时,会对后面跟着的函数的参数值进行预计算; 也就是 编译器编译到此行时,会立刻确定 defer后面跟着函数的参数值,并且是 值传递方式,不是引用传递方式; 这样后续无论 这个参数对应的变量 值怎么改变,都和defer后面的函数无关; 所以 即使 参数 传了一个 函数,也会先计算函数的值 作为参数 进行值引用; 
示例代码 
defer后面跟的是系统自带的 fmt.Println函数 
package mainimport fmtfunc A(v int) int {fmt.Println(A函数,入参为, v)v  1return v
}func UseA(v int) int {defer fmt.Println(defer执行结果, A(v)) // A(v)的值:2    对应解析里的   即使 参数 传了一个 函数,也会先计算函数的值 作为参数 进行值引用;  defer fmt.Println(defer执行结果, v)    //v的值:1   对应解析里的   后续无论 这个参数对应的变量 值怎么改变,都和defer后面的函数无关;fmt.Println(UseA执行)v  5fmt.Println(UseA执行return代码前的最后一行)return v
}func main() {fmt.Println(UseA执行结果, UseA(1)) //6return
}代码执行结果 defer与return关键字执行顺序 
结论 
defer会在return关键字之后, 在返回给调用方前执行; 
示例代码 
package mainimport fmtfunc A(v int) int {fmt.Println(A函数,入参为, v)v  1return v
}func UseOtherA(v int) int {defer fmt.Println(defer执行)return A(v)
}func main() {fmt.Println(UseOtherA执行结果,UseOtherA(1))return
}代码执行结果 defer对 函数的返回值 是否定义变量名的影响 
示例代码 package mainimport fmtfunc A(v int) int {fmt.Println(A函数,入参为, v)v  1return v
}
func B(v int) (result int) {result  vdefer func() {result  A(v)}()return
}func C(v int) int {defer func() {v  A(v)}()return v
}
func main() {fmt.Println(B执行结果, B(1))fmt.Println(C执行结果, C(1))return
}代码执行结果 结论 
定义返回值变量名 的函数,在返回给函数调用方前,这个变量的值都是可以修改的;未定义返回值变量名 的函数, 如上示例的C函数在 return语句执行时,其实是 将v变量 赋值给一个未定义名字的隐藏变量,来完成值传递, 所以后续对v变量的操作对 返回给函数调用结果无任何影响; 示意如下  
defer遇到panic执行情况 
结论 
panic触发后,函数内的后续代码不再执行,在panic之前编译的defer仍然会执行; 
示例代码 
package mainimport fmtfunc PanicT() {defer func() {if err : recover(); err ! nil {fmt.Println(执行recover()方法,尝试恢复程序,错误内容为:, err)}}()defer fmt.Println(defer1)defer fmt.Println(defer2)panic(手动触发panic)defer fmt.Println(defer3,执行不到)
}func main() {PanicT()return
}运行结果 defer中包含panic 
结论 
panic会被覆盖,只保留最新的panic;如: 函数中的panic会被defer的panic覆盖;如: 多个defer都有panic,只有最后执行的defer的panic会保留;其实 defer 后面也是普通函数,那么普通函数遇到panic就会停止运行,执行后续defer的函数; 
代码示例 package mainimport fmtfunc PanicMany() {defer func() {if err : recover(); err ! nil {fmt.Println(执行recover()方法,尝试恢复程序,错误内容为:, err)}}()defer func() {fmt.Println(defer1执行)panic(defer1手动触发panic)fmt.Println(defer1执行不到此处)}()defer func() {fmt.Println(defer2执行)panic(defer2手动触发panic)fmt.Println(defer2执行不到此处)}()panic(手动触发panic)defer fmt.Println(defer3,执行不到)
}func main() {PanicMany()return
}代码执行结果 知识点训练 
func main() {fmt.Println(test1())fmt.Println(test2())fmt.Println(test3())fmt.Println(test4())
}
func test1() (v int) {defer fmt.Println(v) //0return v             //0
}func test2() (v int) {defer func() {fmt.Println(v) //3}()return 3 //3
}func test3() (v int) {defer func() {fmt.Println(v)}() //4defer fmt.Println(v)                    //0defer func(v int) { fmt.Println(v) }(v) //0v  3return 4 //4
}func test4() (v int) {defer func(n int) {fmt.Println(n) //0}(v)return 5 //5
} 
总结 
对于defer执行结果的准确预测, 要了解函数的参数传递方式,函数的返回值是否定义变量名时 编译器的执行过程 等额外的知识点;defer代码编写时经常遇到,常用于 异常捕捉,资源释放等收尾工作;