2010-07-13 82 views
6

我有一個能夠插入(MEF)的應用程序。 插件是導入服務的WPF UserControls。在for循環中分配代理的問題

用戶可以從應用程序的主菜單中選擇想要的插件。

要做到這一點,我用下面的循環:

foreach(IToolPlugin Plugin in ToolPlugins) 
{ 
    Plugin.Init(); 
    MenuItem PluginMenuItem = Plugin.MenuItem; //New MenuItem but with Header set. 
    PluginMenuItem.Click += new RoutedEventHandler(delegate(object o, RoutedEventArgs e) { DoSomething(Plugin.Control);}); 
    PluginsMenu.Items.add(PluginMenuItem); 
} 

,對單個項目的工作非常細。但只要我有一個以上的插件,所有菜單項都會執行最後一個循環的委託。或者至少使用最後一個循環的Plugin.Control。

我該如何解決這個問題?
感謝您的幫助。

+5

我喜歡看到許多這個問題的變化。 – ChaosPandion 2010-07-13 13:14:00

+0

@Chaos - 在這種情況下,您應該投票結束;) – ChrisF 2010-07-13 13:16:29

回答

8

在循環的每次迭代中,您必須在閉包中使用它之前「捕獲」迭代值的值。否則,每個委託中的插件將指向Plugin的最後一個值,而不是創建匿名函數時所持有的值。

您可以從埃裏克利珀這裏閱讀深入的解釋更:

Closing over the loop variable considered harmful - Fabulous Adventures in Coding

總之,寫你的foreach循環正確的做法是:

foreach(IToolPlugin Plugin in ToolPlugins) 
{ 
    Plugin.Init(); 
    MenuItem PluginMenuItem = Plugin.MenuItem; 

    IToolPlugin capturedPlugin = Plugin; 

    PluginMenuItem.Click += 
     new RoutedEventHandler(delegate(object o, RoutedEventArgs e) { 
      DoSomething(capturedPlugin.Control); 
     }); 

    PluginsMenu.Items.add(PluginMenuItem); 
} 
+0

我假設您將包含關於此問題的Eric博客文章的義務鏈接? (關閉循環變量被認爲是有害的。) – 2010-07-13 13:15:58

+0

@Jon Skeet - 是的...努力獲取鏈接。 – 2010-07-13 13:17:42

+0

這個問題我們應該有一個rota :)(因爲它很難搜索,我不認爲它值得作爲一個副本來結束它。) – 2010-07-13 13:23:55