2011-01-19 88 views
0

我有一段代码在Silverlight代码隐藏看起来是这样的:Dispatcher.beginInvoke只通过循环执行最后一次?

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

这个环路上运行一个线程的25倍,从UI线程分开。 testDict对象在执行该方法后最终有25个条目,而图标字典仅存储第25个(最后一个)条目的条目。

这是我第一次使用Dispatcher。这是不是要被称为快速这样的?我所能想到的是,第一次调用委托是在循环的最后一次之后,所以loc对象始终是同一个项目。这是否准确?

回答

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

注使用“拷贝”而不是“LOC”匿名方法内。

欲了解更多详情,请阅读Eric Lippert的博文“关闭被认为有害的循环变量” - part one; part two

+0

谢谢Jon。好文章也。 – Erix 2011-01-19 14:53:46

1

您正在关闭一个迭代变量。解决方法是将loc分配给循环内的临时变量。

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