Question

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.

Was it helpful?

Solution

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

OTHER TIPS

I am just guessing here (have not really tested this,) if you assign to a local value in the for loop code that should capture the value for the iteration.

Random rnd = new Random();

for (Int32 taskIdx = 0; taskIdx < 10; taskIdx++)
{
    var taskIndex = taskIdx;
    _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();
}

This is communally done to capture the value of "this".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top