mea*_*nny 4 c# delegates timer
场景:我正在构建一个调度系统和每个计时器事件,我想运行一个自定义方法而不是通常的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;
}
Run Code Online (Sandbox Code Playgroud)
确实如此简单,实际上确实创造了我的计时器.但是,我希望每个已发生的事件都使用它传入的schedule元素运行.我的问题是,为什么Elapsed事件只传递每个Timer.Elapsed事件的for循环中的最后一个ScheduleElement.
现在我知道是什么修复它,我只是不确定为什么.如果我回滚到原始的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;
}
Run Code Online (Sandbox Code Playgroud)
然后我把它object sender放回原来的物体,然后把它Tag从它上面剥下来,这给了我每个独特计时器的正确时间表.
再说一遍,为什么在所有Timers的foreach循环delegate { }中使用最后一个传递ScheduleElement?
编辑1
Timer类
public TimerEx : Timer {
public TimerEx(double interval) : base(interval) { }
private Object _Tag;
public Object Tag {
get { return _Tag; }
set { _Tag = value; }
}
}
Run Code Online (Sandbox Code Playgroud)
这是因为你在委托中使用了闭包,它关闭了同一个变量,该变量在foreach循环的每次迭代中共享.
有关详细信息,请参阅Eric Lippert的文章Closing over loop variable被认为是有害的.
在这种情况下,您可以使用临时方法轻松修复它:
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); };
Run Code Online (Sandbox Code Playgroud)
请注意,C#5更改了此foreach循环的行为.如果使用最新的编译器进行编译,则问题不再存在.
| 归档时间: |
|
| 查看次数: |
683 次 |
| 最近记录: |