Go——defer Dear 丶 2023-10-09 20:53 9阅读 0赞 ### defer ### Go函数里提供了defer关键字,可以注册多个延迟调用,这些调用以先进后出(FILO)的顺序在函数返回前被执行。这有点类似于Java语言中异常处理中的finaly子句。defer常用于保证一些资源最终一定能够得到回收和释放。 package main func main() { //先进后出 defer func() { println("first") }() defer func() { println("second") }() println("function body") } //结果(先注册后执行) function body second first defer后面必须是函数或方法的调用,不能是语句,否则会expression in defer must be function call错误。 defer函数的实参在注册时通过值拷贝传递进去。下面示例代码中,实参a的值在defer注册时通过值拷贝传递进去,后续语句a++并不会影像defer语句最后的输出结果。 func f() int { a := 0 defer func(i int) { println("defer i=", i) }(a) a++ return a } //defer打印结果 defer i=0 defer语句必须先注册后才能执行,如果defer位于return之后,则defer因为没有注册,不会执行。 package main func main() { defer func() { println("first") }() a := 0 println(a) return defer func() { println("second") }() } //结果 0 first 主动调用os.Exit(int)退出进程时,defer将不再被执行(即使defer已经提前注册) package main import "os" func main() { defer func() { println("defer") }() println("func body") os.Exit(1) } //结果 func body exit status 1 defer的好处是可以在一定程度上避免资源泄露,特别是在有很多return语句,有多个资源需要关闭的场景中,很容易漏掉资源的关闭操作。例如: func CopyFile(dst, src string) (w int64, err error) { fsrc, err := os.Open(src) if err != nil { return } fdst, err := os.Create(dst) if err != nil { //fsrc很容易被忘记关闭 fsrc.Close() return } w, err = io.Copy(fdst, fsrc) fdst.Close() fsrc.Close() return } 使用defer改写后,在打开资源无报错后直接调用defer关闭资源: func CopyFile2(dst, src string) (w int64, err error) { fsrc, err := os.Open(src) if err != nil { return } defer fsrc.Close() fdst, err := os.Create(dst) if err != nil { return } defer fdst.Close() w, err = io.Copy(fdst, fsrc) return } defer语句的位置不当,有可能导致panic,一般defer语句放在错误检查语句之后。 defer也有明显的副作用:defer会推迟资源的释放,defer尽量不要放到循环语句里面,将大函数内部的defer语句单独拆分成一个小函数是一种很好的实践方式。另外,defer相对于普通的函数调用需要间接的数据结构的支持,相对于普通函数调用有一定的性能损耗。 defer中最好不要对有名返回值参数进行操作,否则会引发匪夷所思的结果。
还没有评论,来说两句吧...