【问题标题】:Anonymous method for event handler in for loopfor循环中事件处理程序的匿名方法
【发布时间】:2013-08-24 23:17:45
【问题描述】:

这可以在 for 循环中完成吗?

        TickEventArgs targs1 = new TickEventArgs(lbl1_up_time, _elapsedTime_up1);
        timer_up1.Tick += (sender, e) => Tick(targs1);

        TickEventArgs targs2 = new TickEventArgs(lbl2_up_time, _elapsedTime_up2);
        timer_up2.Tick += (sender, e) => Tick(targs2);

        TickEventArgs targs3 = new TickEventArgs(lbl3_up_time, _elapsedTime_up3);
        timer_up3.Tick += (sender, e) => Tick(targs3);

        TickEventArgs targs4 = new TickEventArgs(lbl4_up_time, _elapsedTime_up4);
        timer_up4.Tick += (sender, e) => Tick(targs4);

        TickEventArgs targs5 = new TickEventArgs(lbl5_up_time, _elapsedTime_up5);
        timer_up5.Tick += (sender, e) => Tick(targs5);

这不起作用,因为 i 超出范围 (5)

        targs[0] = new TickEventArgs(lbl1_up_time, _elapsedTime_up1);
        targs[1] = new TickEventArgs(lbl2_up_time, _elapsedTime_up2);
        targs[2] = new TickEventArgs(lbl3_up_time, _elapsedTime_up3);
        targs[3] = new TickEventArgs(lbl4_up_time, _elapsedTime_up4);
        targs[4] = new TickEventArgs(lbl5_up_time, _elapsedTime_up5);

        timers[0] = timer_up1;
        timers[1] = timer_up2;
        timers[2] = timer_up3;
        timers[3] = timer_up4;
        timers[4] = timer_up5;

        int i = 0;

        for (i = 0; i <= 4; i++)
        {
            timers[i].Tick += (sender, e) => Tick(targs[i]);
        } 

【问题讨论】:

  • 这是来自 lambda 表达式; i 在所有人之间共享。到函数执行时,它们基本上被称为timers[i].Tick += (sender, e) =&gt; Tick(targs[5])。声明一个本地 int locali = i 并在你的行中使用它。
  • @ChrisSinclair 将其作为答案发布。

标签: c# winforms event-handling


【解决方案1】:

这是来自 lambda 表达式; iis shared between all of them。到函数执行时,它们基本上被称为timers[i].Tick += (sender, e) =&gt; Tick(targs[5])

为避免这种情况,请创建一个局部范围的变量 (int locali = i) 并在您的行中使用它。这将确保每个 lambda 表达式实际上都获得了您期望的值。

for (i = 0; i <= 4; i++)
{
    int locali = i;
    timers[locali].Tick += (sender, e) => Tick(targs[locali]);
} 

i 在退出之前从循环的最后一次迭代变为 5。当然,您没有 targs[5] 元素,因此它会抛出 IndexOutOfRangeException

从技术上讲,您不需要将 locali 用于 timers[i].Tick 部分,因为它会立即进行评估,但我个人觉得将两者混合使用会令人困惑。


关于概念的一些补充阅读:

The foreach identifier and closures

Closing over the loop variable considered harmful

【讨论】:

  • 感谢您的快速回答。现在可以正常工作。顺便说一句,您知道是否有一种方法可以简化数组的分配,即所有目标都将在一个语句中完成?
  • @dirtyw0lf:但是您将它们全部分配给不同的计时器。您真的只想要一个单个计时器来清除整个targs 数组吗?
  • 不,只是想知道我是否可以一次性完成所有计时器的数组分配
  • @dirtyw0lf:您的意思是跳过for 循环并有这样一行:timers[magic].Tick += (sender, e) =&gt; Tick(targs[magic])?我不这么认为。您可以创建一个单独的对象来为您处理所有 Timer/TickEventArgs 实例,这样您就有了一个干净的 API,然后您只需一个事件即可分配一个 Action&lt;TimerEventArgs&gt; 委托给。
  • @dirtyw0lf:啊。可能有反射,但它会很乱。您还可以存储控件的集合,这样您就可以像up_time_labels[X]elapsedTimeUps[X] 一样查找它们,但是您仍然必须以与现在类似的方式填充这些集合。无论如何,你得到的并不是太糟糕;如果没有更好地了解代码的全部范围和用法,很难真正提出更多改进建议。也许你可以在codereview.stackexchange.com
【解决方案2】:

在这种情况下只有一个i,并且所有的 lambda 都捕获相同的值。使用作用域为循环的局部变量,以便每个 lambda 具有不同的副本

for (i = 0; i <= 4; i++)
{
  int j = i;
  timers[j].Tick += (sender, e) => Tick(targs[j]);
}

【讨论】:

    猜你喜欢
    • 2018-10-06
    • 1970-01-01
    • 2012-10-12
    • 1970-01-01
    • 2011-09-09
    • 2015-04-05
    • 2011-09-21
    • 2010-11-26
    • 1970-01-01
    相关资源
    最近更新 更多