为什么我的代表只使用我的foreach循环中的最后一项? [英] Why does my delegate only use the last item from my foreach loop?

查看:139
本文介绍了为什么我的代表只使用我的foreach循环中的最后一项?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景:我正在构建一个调度系统,每个计时器事件我想运行一个自定义方法,而不是通常的 Timer.Elapsed 事件

Scenario: I am building a scheduling system and each timer event I wanted to run a custom method instead of the usual Timer.Elapsed event.

所以我写了这样的东西。

So I wrote something like this.

foreach (ScheduleElement schedule in schedules) {
    TimeSpan timeToRun = CalculateTime(schedule);
    schedule.Timer = new Timer(timeToRun.TotalMilliseconds);
    schedule.Timer.Elapsed += delegate { Refresh_Timer(schedule); };
    schedule.Timer.AutoReset = true;
    schedule.Timer.Enabled = true;
}

确定是如此简单,以至于确实创建了我的计时器。但是,我希望每个经过的事件都使用传入的schedule元素运行。我的问题是,为什么Elapsed事件仅对每个Timer.Elapsed事件在for循环中仅传递最后一个ScheduleElement。

Ok so simple enough that actually did create my timers. However, I wanted each elapsed event to run using the schedule element that it passed in. My question is, why does the Elapsed event only pass in the last ScheduleElement in the for loop for every single Timer.Elapsed event.

现在我知道它能解决什么,我不确定为什么。如果我回滚到原始的Timer.Elapsed事件,并使用自己的类扩展Timer类,则可以解决该问题。

Now I know what fixes it, I am just not sure why. If I roll back to the original Timer.Elapsed event and extend the Timer class with my own class I can work around it. Like so.

修复:

foreach (ScheduleElement schedule in schedules) {
    TimeSpan timeToRun = CalculateTime(schedule);
    schedule.Timer = new TimerEx(timeToRun.TotalMilliseconds);
    schedule.Timer.Elapsed +=new System.Timers.ElapsedEventHandler(Refresh_Timer);
    schedule.Timer.Tag = schedule;
    schedule.Timer.AutoReset = true;
    schedule.Timer.Enabled = true;
}

然后我投放对象发送者返回其原始对象,并从中窃取 Tag 属性,这为我提供了每个唯一计时器的正确时间表。

I then cast the object sender back into its original object, and thieve the Tag property off of it which gives me my correct schedule for each unique timer.

同样,为什么使用 delegate {} 只传递最后一个 ScheduleElement 是否在所有计时器的foreach循环中?

So again, why does using a delegate { } only pass in the last ScheduleElement in the foreach loop for all Timers?

编辑1

Timer类

public TimerEx : Timer {

    public TimerEx(double interval) : base(interval) { }

    private Object _Tag;

    public Object Tag {
        get { return _Tag; }
        set { _Tag = value; }
    }
}


推荐答案

这是因为您在委托中使用了闭包,并且它关闭了相同的变量,该变量在foreach循环的每次迭代中都是共享的。

This is because you're using closure in your delegate, and it closes over the same variable, which is shared for each iteration of the foreach loop.

有关详细信息,请参见埃里克·利珀特(Eric Lippert)的文章循环变量被认为是有害的

For details, see Eric Lippert's article Closing over the loop variable considered harmful.

在这种情况下,您可以使用临时变量轻松对其进行修复:

In this case, you can easily fix it with a temporary:

foreach (ScheduleElement schedule in schedules) {
    TimeSpan timeToRun = CalculateTime(schedule);
    schedule.Timer = new Timer(timeToRun.TotalMilliseconds);

    // Make a temporary variable in the proper scope, and close over it instead
    var temp = schedule;
    schedule.Timer.Elapsed += delegate { Refresh_Timer(temp); };

请注意,C#5更改了 foreach 循环。如果使用最新的编译器进行编译,则该问题不再存在。

Note that C# 5 changes this behavior for foreach loops. If you compile this with the latest compilers, the issue no longer exists.

这篇关于为什么我的代表只使用我的foreach循环中的最后一项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆