【问题标题】:Problem with assigning delegates in for-loop [duplicate]在for循环中分配代表的问题[重复]
【发布时间】:2010-07-13 13:09:54
【问题描述】:

我有一个支持插件 (MEF) 的应用程序。 插件是导入服务的 WPF 用户控件。

用户可以从应用程序的主菜单中选择想要的插件。

为此,我使用以下循环:

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

这对于单个项目非常有效。但是只要我有超过 1 个插件,所有菜单项都会执行最后一个循环的委托。或者至少使用最后一个循环的 Plugin.Control。

我该如何解决这个问题?
感谢您的帮助。

【问题讨论】:

  • 我喜欢看到这个问题的许多变化。
  • @Chaos - 在这种情况下你应该投票关闭 ;)

标签: c# delegates closures event-handling


【解决方案1】:

在循环的每次迭代中,您必须先“捕获”迭代值的值,然后才能在闭包中使用它。否则,每个委托中的 Plugin 将指向 Plugin 的最后一个值,而不是它在创建匿名函数时所持有的值。

您可以在此处阅读 Eric Lippert 的更深入解释:

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

【讨论】:

  • 我假设您将包含指向 Eric 关于此事的博客文章的强制性链接? (关闭被认为有害的循环变量。)
  • 我们应该有一个关于这个问题的列表 :) (由于很难搜索,我认为不值得将其作为副本关闭。)
  • @Jon Skeet - 很难说。我刚刚快速搜索了 c#closure foreach 并获得了大量点击(还在问题中添加了闭包标签)。我认为部分问题在于人们没有意识到这些是闭包。
  • 当然。如果你知道这是一个关闭,那么你就成功了一半。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 2013-01-05
  • 2011-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多