C# 事件处理程序委托中的闭包?
我目前有函数式编程背景,所以如果我不理解 C# 中的闭包,请原谅我。
我有以下代码来动态生成获取匿名事件处理程序的按钮:
for (int i = 0; i < 7; i++)
{
Button newButton = new Button();
newButton.Text = "Click me!";
newButton.Click += delegate(Object sender, EventArgs e)
{
MessageBox.Show("I am button number " + i);
};
this.Controls.Add(newButton);
}
我期望文本 "I am Button number " + i
将以 i
的值关闭for 循环的迭代。然而,当我实际运行该程序时,每个按钮都会显示我是按钮号 7
。我缺少什么?我用的是VS2005。
编辑:所以我想我的下一个问题是,如何捕获价值?
I am coming from a functional-programming background at the moment, so forgive me if I do not understand closures in C#.
I have the following code to dynamically generate Buttons that get anonymous event handlers:
for (int i = 0; i < 7; i++)
{
Button newButton = new Button();
newButton.Text = "Click me!";
newButton.Click += delegate(Object sender, EventArgs e)
{
MessageBox.Show("I am button number " + i);
};
this.Controls.Add(newButton);
}
I expected the text "I am button number " + i
to be closed with the value of i
at that iteration of the for loop. However, when I actually run the program, every Button says I am button number 7
. What am I missing? I am using VS2005.
Edit: So I guess my next question is, how do I capture the value?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
要获得此行为,您需要在本地复制变量,而不是使用迭代器:
更详细地讨论推理 在这个问题中。
To get this behavior, you need to copy the variable locally, not use the iterator:
The reasoning is discussed in much greater detail in this question.
尼克说得对,但我想在这个问题的文本中更好地解释一下为什么。
问题不在于关闭,而在于关闭。这是 for 循环。该循环仅为整个循环创建一个变量“i”。它不会为每次迭代创建新变量“i”。 注意: 据报道,这一点在 C# 5 中已发生变化。
这意味着当您的匿名委托捕获或关闭该“i”变量时,它会关闭由所有对象共享的一个变量。按钮。当你真正点击这些按钮中的任何一个时,循环已经完成将该变量增加到 7。
我可能会做的与 Nick 的代码不同的一件事是使用一个字符串作为内部变量,并预先构建所有这些字符串,而不是比按下按钮时,像这样:
这只是用一点内存(保留更大的字符串变量而不是整数)来换取稍后的一点CPU时间......这取决于您的应用程序更重要的事情。
另一种选择是根本不手动编码循环:
我很喜欢最后一个选项,不是因为它删除了循环,而是因为它让您开始思考从数据源构建此控件。
Nick has it right, but I wanted to explain a little better in the text of this question exactly why.
The problem isn't the closure; it's the for-loop. The loop only creates one variable "i" for the entire loop. It does not create a new variable "i" for each iteration. Note: This has reportedly changed for C# 5.
This means when your anonymous delegate captures or closes over that "i" variable it's closing over one variable that is shared by all the buttons. By the time you actually get to click any of those buttons the loop has already finished incrementing that variable up to 7.
The one thing I might do differently from Nick's code is use a string for the inner variable and build all those strings up front rather than at button-press time, like so:
That just trades a little bit of memory (holding on to larger string variables instead of integers) for a little bit of cpu time later on... it depends on your application what matters more.
Another option is to not manually code the loop at all:
I like this last option not so much because it removes the loop but because it starts you thinking in terms of building this controls from a data source.
您创建了七个委托,但每个委托都持有对同一个 i 实例的引用。
MessageBox.Show
函数仅在单击按钮时调用。单击按钮时,循环已完成。因此,此时i
将等于 7。试试这个:
You have created seven delegates, but each delegate holds a reference to the same instance of i.
The
MessageBox.Show
function is only called when the button is clicked. By the time the button has clicked, the loop has completed. So, at this pointi
will be equaling seven.Try this:
闭包捕获变量而不是值。这意味着在执行委托时,即在循环结束后的某个时间,i 的值为 6。
要捕获一个值,请将其分配给循环体中声明的变量。在循环的每次迭代中,将为其中声明的每个变量创建一个新实例。
Jon Skeet 的关于闭包的文章有更深入的解释和更多示例。
The closure captures the variable not the value. This means that by the time the delegate is executed, ie sometime after the end of the loop, the value of i is 6.
To capture a value, assign it to a variable declared in the loop body. On each iteration of the loop, a new instance will be created for each variable declared within it.
Jon Skeet's articles on closures has a deeper explanation and more examples.
当你点击任何按钮时,它们都已经从 1 到 7 生成了,所以它们都将表达 i 的最终状态,即 7。
By the time you click any button, they have all been generated from 1 thru 7, so they will all express the final state of i which is 7.