简体   繁体   English

Dispatcher.begin仅通过循环调用最后一次执行吗?

[英]Dispatcher.beginInvoke only executing the last time through a loop?

I've got a piece of code in a Silverlight code-behind that looks something like this: 我在Silverlight代码中有一段代码,看起来像这样:

foreach (MapLocation loc in e.Result)
        {

            testDict[loc.ElemId] = loc.ToString();

            this.Dispatcher.BeginInvoke(delegate()
            {
                Image icon = new Image();
                icon.SetValue(Image.SourceProperty, nurseIconSource);
                Canvas.SetLeft(icon, (double)loc.X * MAP_SCALE);
                Canvas.SetTop(icon, MAP_HEIGHT - (double)loc.Y * MAP_SCALE);
                icons[loc.ElemId] = icon;
                MainCanvas.Children.Add(icon);
            });
        }
    }

This loop is running 25 times on a thread separate from the UI thread. 此循环在与UI线程分开的线程上运行25次。 The testDict object ends up with all 25 entries after execution of the method, while the icons dictionary only stores an entry for the 25th (last) item. 执行该方法后,testDict对象最终包含所有25个条目,而图标字典仅存储第25个(最后一个)项目的条目。

This is my first time using Dispatcher. 这是我第一次使用Dispatcher。 Is it not meant to be called rapid-fire like this? 难道不应该被称为“速射”吗? All I can think of is that the first time the delegate is invoked is after the last time through the loop, so the loc object is always the same item. 我能想到的是,第一次调用委托是在循环的最后一次之后,因此loc对象始终是同一项目。 Is this accurate? 这个准确吗?

It's the old "don't capture a loop variable" problem. 这是古老的“不捕获循环变量”问题。 This catches a lot of people out. 这吸引了很多人。

Basically, when the delegate executes it's always using the current value of loc ... and if you've already finished the loop before the delegates execute, that'll mean the last value of loc is displayed many times, basically. 基本上,当委托执行时,它总是使用loc当前值...并且,如果您在委托执行之前已经完成了循环,那么这基本上意味着loc的最后一个值会显示多次。

The solution is to take a copy of the loop variable: 解决方案是复制循环变量:

foreach (MapLocation loc in e.Result)
{
    MapLocation copy = loc;
    testDict[loc.ElemId] = loc.ToString();

    this.Dispatcher.BeginInvoke(delegate()
    {
        Image icon = new Image();
        icon.SetValue(Image.SourceProperty, nurseIconSource);
        Canvas.SetLeft(icon, (double)copy.X * MAP_SCALE);
        Canvas.SetTop(icon, MAP_HEIGHT - (double)copy.Y * MAP_SCALE);
        icons[copy.ElemId] = icon;
        MainCanvas.Children.Add(icon);
    });
}

Note the use of "copy" instead of "loc" inside the anonymous method. 注意匿名方法中使用“ copy”而不是“ loc”。

For more details, read Eric Lippert's blog posts on "Closing over the loop variable considered harmful" - part one ; 有关更多详细信息,请阅读Eric Lippert的博客文章“关闭认为有害的循环变量”-第一部分 part two . 第二部分

You are closing over an iteration variable. 您正在关闭一个迭代变量。 The solution is to assign loc to a temporary variable inside the loop. 解决方案是将loc分配给循环内的临时变量。

    foreach (MapLocation location in e.Result)
    {
        //assign temp variable here.
        MapLocation loc = location;
        //...
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM