2011-01-19 85 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; 
     //... 
    }