Why does my delegate only use the last item from my foreach loop?(为什么我的委托只使用我的 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 循环中的最后一项?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么我的委托只使用我的 foreach 循环中的最后一项?


- 如何用自己压缩一个 IEnumerable 2022-01-01
- MoreLinq maxBy vs LINQ max + where 2022-01-01
- WebMatrix WebSecurity PasswordSalt 2022-01-01
- 带有服务/守护程序应用程序的 Microsoft Graph CSharp SDK 和 OneDrive for Business - 配额方面返回 null 2022-01-01
- 输入按键事件处理程序 2022-01-01
- Web Api 中的 Swagger .netcore 3.1,使用 swagger UI 设置日期时间格式 2022-01-01
- 良好实践:如何重用 .csproj 和 .sln 文件来为 CI 创建 2022-01-01
- C# 中多线程网络服务器的模式 2022-01-01
- 在哪里可以找到使用中的C#/XML文档注释的好例子? 2022-01-01
- C#MongoDB使用Builders查找派生对象 2022-09-04