为啥我的委托只使用我的 foreach 循环中的最后一项?
Posted
技术标签:
【中文标题】为啥我的委托只使用我的 foreach 循环中的最后一项?【英文标题】:Why does my delegate only use the last item from my foreach loop?为什么我的委托只使用我的 foreach 循环中的最后一项? 【发布时间】:2013-07-15 08:33:57 【问题描述】:场景:我正在构建一个调度系统,我希望每个计时器事件都运行一个自定义方法,而不是通常的Timer.Elapsed
事件。
所以我写了这样的东西。
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 事件传递。
现在我知道是什么解决了它,我只是不知道为什么。如果我回滚到原始 Timer.Elapsed 事件并用我自己的类扩展 Timer 类,我可以解决它。像这样。
解决方法:
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
属性,这为我提供了每个唯一计时器的正确时间表。
那么,为什么在所有计时器的 foreach 循环中只使用delegate
只传递最后一个ScheduleElement
?
编辑 1
定时器类
public TimerEx : Timer
public TimerEx(double interval) : base(interval)
private Object _Tag;
public Object Tag
get return _Tag;
set _Tag = value;
【问题讨论】:
研究“闭环” 检查这个:***.com/questions/271440/… 【参考方案1】:这是因为您在委托中使用了闭包,并且它关闭了同一个变量,该变量为 foreach 循环的每次迭代共享。
详情见Eric Lippert的文章Closing over the loop variable considered harmful。
在这种情况下,您可以轻松地使用临时修复它:
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
循环的这种行为。如果你用最新的编译器编译它,问题就不再存在了。
【讨论】:
在哪里可以找到有关使用最新编译器的更多信息?我不太确定我的 VS 使用的是什么,我有 2010 年。 @meanbunny 您需要使用 VS 2012 或更高版本才能获得新行为。它在 VS 2012 发布的 C# 5 中进行了更改。 好的,谢谢,很好的回答,我真的很感激!距离我接受还有 4 分钟。 也许值得注意的是,ReSharper 将此强调为“访问修改后的闭包”警告,并建议引入临时/迭代范围变量。以上是关于为啥我的委托只使用我的 foreach 循环中的最后一项?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我的 foreach 循环与 users.json 文件中的键不匹配
当我使用 for 循环时,为啥我的代码没有在 forEach 中等待? [复制]