为什么我的委托只使用我的 foreach 循环中的最后一项?

Why does my delegate only use the last item from my foreach loop?(为什么我的委托只使用我的 foreach 循环中的最后一项?)
本文介绍了为什么我的委托只使用我的 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;
}

好的,很简单,实际上确实创建了我的计时器.但是,我希望每个 elapsed 事件都使用它传入的 schedule 元素运行.我的问题是,为什么 Elapsed 事件仅在 for 循环中的最后一个 ScheduleElement 中为每个 Timer.Elapsed 事件传递.

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;
}

然后,我将 object sender 转换回其原始对象,并从其中窃取 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 { } 只为所有 Timers 传入 foreach 循环中的最后一个 ScheduleElement?

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

编辑 1

定时器类

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 循环中的最后一项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)