Dispatcher.beginInvoke只执行最后一次循环?

Eri*_*rix 0 c# dispatcher threadpool

我在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);
            });
        }
    }
Run Code Online (Sandbox Code Playgroud)

此循环在与UI线程分开的线程上运行25次.执行该方法后,testDict对象以所有25个条目结束,而图标字典仅存储第25个(最后一个)项目的条目.

这是我第一次使用Dispatcher.它不是故意被称为快速射击吗?我能想到的是,第一次调用委托是在最后一次循环之后,因此loc对象始终是同一个项目.这准确吗?

Jon*_*eet 5

它是旧的"不捕获循环变量"的问题.这吸引了很多人.

基本上,当委托执行它时总是使用当前loc...并且如果你已经在委托执行之前完成了循环,那么这意味着最后的值loc显示了很多次,基本上.

解决方案是获取循环变量的副本:

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);
    });
}
Run Code Online (Sandbox Code Playgroud)

注意在匿名方法中使用"copy"而不是"loc".

有关更多详细信息,请阅读Eric Lippert关于"关闭循环变量被认为有害"的博客文章 - 第一部分 ; 第二部分.