我在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);
});
}
}
此循环在与UI线程分开的线程上运行25次。执行该方法后,testDict对象以所有25个条目结束,而图标字典仅存储第25个(最后一个)项目的条目。
这是我第一次使用Dispatcher。它不是故意被称为快速射击吗?我能想到的是,第一次调用委托是在最后一次循环之后,因此loc对象始终是同一个项目。这准确吗?
答案 0 :(得分:5)
这是旧的“不捕获循环变量”问题。这吸引了很多人。
基本上,当委托执行时总是使用{em>当前值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);
});
}
请注意在匿名方法中使用“copy”而不是“loc”。
有关详细信息,请阅读Eric Lippert关于“关闭环路变量被视为有害”的博客文章 - part one; part two
答案 1 :(得分:1)
您正在关闭迭代变量。解决方案是将loc分配给循环内的临时变量。
foreach (MapLocation location in e.Result)
{
//assign temp variable here.
MapLocation loc = location;
//...
}