36

这是我的代码(运行):

package main

import "fmt"

func main() {
    var whatever [5]struct{}

    for i := range whatever {
        fmt.Println(i)
    } // part 1

    for i := range whatever {
        defer func() { fmt.Println(i) }()
    } // part 2

    for i := range whatever {
        defer func(n int) { fmt.Println(n) }(i)
    } // part 3
}

输出:

0 1 2 3 4 4 3 2 1 0 4 4 4 4 4

问题:第 2 部分和第 3 部分有什么区别?为什么第 2 部分输出“44444”而不是“43210”?

4

2 回答 2

45

“part 2”闭包捕获变量“i”。当闭包中的代码(稍后)执行时,变量“i”具有它在 range 语句的最后一次迭代中具有的值,即。'4'。因此

4 4 4 4 4

输出的一部分。

“第 3 部分”在其闭包中不捕获任何外部变量。正如规格所说:

每次执行“defer”语句时,调用的函数值和参数都会像往常一样进行评估并重新保存,但不会调用实际函数。

因此,每个延迟函数调用都有不同的“n”参数值。它是执行 defer 语句时“i”变量的值。因此

4 3 2 1 0

部分输出,因为:

...延迟调用在周围函数返回之前以 LIFO 顺序执行...


需要注意的关键点是defer语句执行时' defer f()'中的'f()'并没有被执行

'defer f(e)' 中的表达式 'e' 在defer 语句执行时被计算。

于 2013-04-15T08:32:15.763 回答
1

为了加深对 的理解,我想再举一个例子,defer mechanish先按原样运行这段代码,然后切换标记为 (A) 和 (B) 的语句的顺序,然后自己查看结果。

package main

import (
    "fmt"
)

type Component struct {
    val int
}

func (c Component) method() {
    fmt.Println(c.val)
}

func main() {
    c := Component{}
    defer c.method()  // statement (A)
    c.val = 2 // statement (B)
}

我一直想知道在这里应用的正确关键字或概念是什么。看起来表达式c.method被评估,因此返回一个绑定到组件“c”的实际状态的函数(就像拍摄组件内部状态的快照)。我想答案不仅涉及defer mechanish如何funtions with value or pointer receiver工作。请注意,如果您将名为methoda的 func 更改为pointer receiverdefer 将 c.val 打印为 2,而不是 0,也会发生这种情况。

于 2018-11-09T19:08:47.883 回答