推迟使用说明

Defer usage clarification

假设我有以下功能

func printNumbers(){
 var x int

 defer fmt.Println(x)

 for i := 0; i < 5; i++{
  x++
 }
}

正如specification中所说:

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.

显然,函数执行结束时会打印出零。 但是如果我想打印出变量x的最终值怎么办?

我想出了以下解决方案:

func printNumbers(){
  var x int

  printVal := func(){
    fmt.Println(x)
  }

  defer printVal()

  for i := 0; i < 5; i++{
    x++
  }
}

所以我想知道是否有更好的方法来解决这个问题。

通常重要的是 x 不能作为您要延迟的函数的参数,因为它们是在执行 defer 时计算的。

1) 匿名函数

这是一个使用匿名函数的解决方案:

defer func() { fmt.Println(x) }()

这里x不是延迟匿名函数的参数,所以不会被求值。仅当执行匿名函数并调用 fmt.Println().

2) 带指针

使用指向x 的指针(如&x)会起作用,因为只评估地址,最后指向的值当然是5。这个问题是 fmt.Println() 不会打印指向的值,而是指针本身。

但要演示其工作原理,请参阅此辅助函数:

func Print(i *int) {
    fmt.Println(*i)
}

并使用它:

defer Print(&x) // Will print 5 at the end

3) 使用自定义类型

这类似于指针解决方案,但不需要辅助函数。但它确实需要您编写 String() 方法:

type MyInt int

func (m *MyInt) String() string {
    return strconv.Itoa(int(*m))
}

并使用它:

var x MyInt

defer fmt.Println(&x)

for i := 0; i < 5; i++ {
    x++
}

当执行defer语句时,只会计算指针([=19=的地址,*Myint的类型)。由于类型 *MyInt 实现了 fmt.Stringerfmt.Println() 将调用它的 String() 方法。

4) 环绕

这也类似于指针解决方案,它甚至不会像您期望的那样只打印 5,但是:

#2 的问题是 fmt.Println() 将打印指针而不是指向的值(我们用自己的 Print() 函数解决了这个问题)。然而,还有其他类似于指针的类型,fmt.Println() 将打印它们的内容。

所以让我们将变量包装到一个切片中,看看会发生什么:

x := []int{0}

defer fmt.Println(x)

for i := 0; i < 5; i++ {
    x[0]++
}

打印:

[5]

看到 5 的原因是切片是一个描述符。当计算 defer 时,会生成切片的副本(执行时将传递给 fmt.Println()),但它引用相同的底层数组。

另请注意,如果指针是指向结构、数组、切片、映射的指针,fmt.Println() 会打印指向的内容,因此以下代码也有效:

x := struct{ i int }{}

defer fmt.Println(&x)

for i := 0; i < 5; i++ {
    x.i++
}

并打印:

&{5}

如果 defer 有参数,它们将在 defer 语句的行进行计算;以下代码段对此进行了说明,其中 defer 将打印 0:

func printNumber() {
   i := 0
   defer fmt.Println(i) // will print 0
   i++
   return
}

如果要将语句或函数的执行推迟到封闭(调用)函数结束,则可以将匿名函数用作延迟语句。这是一个更新的示例:

func printNumbers() {
    x := 0
    defer func() { fmt.Println(x) }()
    for i:=0; i < 5; i++ {
        x++;
    }
    return
}

http://play.golang.org/p/YQGQ_8a0_9