I want a piece of test code which creates a collection of tasks and then calls a method with each task's index at some random time in the near future.
I wrote it like this:
Random rnd = new Random();
for (Int32 taskIndex = 0; taskIndex < 10; taskIndex++)
{
_tasks.Add(String.Format("Task_{0}", taskIndex));
Progress.AddItem(_tasks[taskIndex]);
Int32 timeDelay = 5 + rnd.Next(10);
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromSeconds(timeDelay);
dispatcherTimer.Tick += (sndr, eArgs) =>
{
dispatcherTimer.Stop();
Progress.SetItemStatus(taskIndex, Status.Executing);
};
dispatcherTimer.Start();
}
When it runs it always passes 10 to Progress.SetItemStatus() as the taskIndex. I understand why this is (thanks, Mr Skeet) - because taskIndex is captured within the anonymous method and uses its value at the time the method is executed.
What I'd like to know is the most elegant way to achieve my goal, i.e., passing the value in the instance at the time the Tick event is set.
You are closing over the loop variable. See Eric Lippert’s Blog post for more info.
Basicly, the defered execution is using taskIndex, which by the time it gets executed will always be 10 (or whatever the last value is).
Random rnd = new Random();
for (Int32 taskIndex = 0; taskIndex < 10; taskIndex++)
{
Int32 tempIndex = taskIndex;
_tasks.Add(String.Format("Task_{0}", tempIndex));
Progress.AddItem(_tasks[tempIndex]);
Int32 timeDelay = 5 + rnd.Next(10);
DispatcherTimer dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = TimeSpan.FromSeconds(timeDelay);
dispatcherTimer.Tick += (sndr, eArgs) =>
{
dispatcherTimer.Stop();
Progress.SetItemStatus(tempIndex, Status.Executing);
};
dispatcherTimer.Start();
}