【问题标题】:Dynamically Generating Buttons that Call a Lambda Function -- Variable Scope动态生成调用 Lambda 函数的按钮——变量范围
【发布时间】:2011-08-20 11:38:58
【问题描述】:

我有someFunction(int) 的情况,我需要以编程方式生成n 按钮来调用它。这意味着我想创建按钮B1B2、...Bn,点击时调用someFunction(1)someFunction(2)、...someFunction(n)

这就是我尝试这样做的方式(半伪代码):

for (int i = 1; i <= n; i++) {
  Button b = new Button();
  b.Caption = "Value " + n; // non-WPF: b.Text = "Value " + n;
  b.Click += (sender, event) => {
    someFunction(i);
  }
}

让我感到困扰的是,当我单击第一个按钮 (B1) 时,使用someFunction(i) 上的调试器,它告诉我它正在调用someFunction(n + 1)

我不确定这是为什么,或者如何解决。我使用的解决方法是使用someFunction(i),而不是someFunction(int.Parse(i.ToString())(创建i 的副本)。但这对我来说似乎很可疑,因为整数应该是值类型。

【问题讨论】:

标签: c# delegates parameter-passing lambda


【解决方案1】:

我相信您明白为什么会发生这种情况。问题是它捕获变量i 本身,而不是它的值。对我来说似乎更好的解决方法(没有 toString 和 int.parse)是声明另一个复制 i

的本地变量
for (int i = 1; i <= n; i++) {
Button b = new Button();
  b.Caption = "Value " + n; // non-WPF: b.Text = "Value " + n;
  int locali = i;
  b.Click += (sender, event) => {
    someFunction(locali);
  }
}

这样捕获的变量将为locali,并且在整个循环中保持不变。

【讨论】:

  • 这在精神上是正确的,但locali 应该在代表的外部,而不是内部。
  • @JSBangs 为什么要在外面声明?范围仅限于委托调用。
  • @JSBangs 你是绝对正确的。我想当我把它放在其他代码之间时我并没有太在意。 @ashes999 它应该在外面,因为如果它在委托中,i 仍将被捕获,最终结果将是相同的。
  • 尽管我很喜欢这个解决方案,但这似乎是一个 hack/trick/work-around,而不是一个合适的解决方案。有没有更好的方法来做到这一点? (如果这是唯一的答案并且有效,我会接受。)
  • 你可以阅读这个。 msdn.microsoft.com/en-us/library/bb763133.aspx 适用于 VB,但原理相同,它们的工作方式相同。局部变量方式也是微软对问题的解决方案(意外行为)。编辑:另一个链接:rongchaua.net/blog/…
【解决方案2】:

Stormbreaker 是正确的,有关更多信息,请参阅 Eric Lippert 对此问题的回答

C# lambda, local variable value not taken when you think?

【讨论】:

    猜你喜欢
    • 2017-04-21
    • 1970-01-01
    • 2013-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    相关资源
    最近更新 更多