如何通过lamba表达式传递捕获的变量,就好像它没有被捕获一样?

时间:2013-05-31 19:21:28

标签: c# closures anonymous-methods

我想要一段测试代码,它创建一个任务集合,然后在不久的将来随机时间调用每个任务索引的方法。

我是这样写的:

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

当它运行时,它总是将10传递给Progress.SetItemStatus()作为taskIndex。我明白为什么会这样(谢谢,Skeet先生) - 因为taskIndex是在匿名方法中捕获的,并在执行方法时使用它的值。

我想知道的是实现目标的最优雅方式,即在设置Tick事件时传递实例中的值。

2 个答案:

答案 0 :(得分:3)

您正在关闭循环变量。有关详细信息,请参阅Eric Lippert’s Blog帖子。

基本上,deferred执行是使用taskIndex,它在执行时总是10(或者是最后一个值)。

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

答案 1 :(得分:0)

我只是在这里猜测(还没有真正测试过这个),如果你在for循环代码中分配一个应该捕获迭代值的本地值。

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

这是为了捕捉“这个”的价值而共同完成的。