我无法将变量转换为匿名函数。有解决方案吗?
import "github.com/lxn/walk"
***
var openAction [12]*walk.Action
for i := 0; i < 12; i++ {
openBmp, err := walk.NewBitmapFromFile(_films[i][0])
if err != nil {
log.Printf("Open bitmap for buildBody() :%v\n", err)
}
openAction[i] = walk.NewAction()
openAction[i].SetImage(openBmp)
openAction[i].SetText(_films[i][2])
openAction[i].Triggered().Attach( func(){
exec(i)
})
mw.ToolBar().Actions().Add(openAction[i])
}
exec(i)其中我总是= 11
答案 0 :(得分:10)
for i := 0; i < 12; i++ {
i := i
...
看起来很疯狂,这是你将在Go代码中看到的东西。它来自闭包的工作方式和变量的作用方式。你的匿名函数是一个捕获我的闭包。具体来说,它捕获一个名为i的变量,而不是i的当前值,它捕获我在范围内的任何内容。在原始代码中,这是循环变量,它对于循环的每次迭代都是相同的变量。所有闭包都捕获了相同的变量。 i := i
的添加在每次迭代时声明一个新变量。现在每个闭包都将捕获这个新变量,并且在每次迭代时它将是一个不同的变量。
更详细一点,循环变量i的范围是for语句。这包括循环块,但由于循环变量i的声明在块之外,因此在块内声明具有相同名称的新变量是合法的,并在块中的该点创建新变量。然后对循环变量进行阴影处理。通常,这样声明的变量会出现在堆栈中,但在这种情况下,编译器转义分析会发现当块在块的末尾超出范围时,闭包仍然引用此块变量,因此变量放在堆。在每次迭代时,重新输入块,并在堆上放置一个新变量。
答案 1 :(得分:6)
我认为这会让你得到你想要的东西:
openAction[i].Triggered().Attach(func(x int) func() {
return func() { exec(x) }
}(i))
诀窍是让你的匿名函数返回一个匿名函数,每个创建的函数都会包含 i 的每个值。
答案 2 :(得分:4)
你正在遇到围绕着循环的怪癖。循环中的i变量不是每次迭代的新变量。因为这样你所有的闭包都关闭了它们下面的值正在变化的同一个变量。当你的代码在循环之后运行时,所有函数都会看到它们关闭的i的值为11。
解决方案是将i传递给一个函数,然后返回另一个关闭函数arg的函数。这就是Adam Crosslands解决方案有效的原因。