Проблема с назначением делегатов в цикле for - PullRequest
6 голосов
/ 13 июля 2010

У меня есть приложение, которое поддерживает плагины (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 последнего цикла.

Как я могу это исправить?
Спасибо за любую помощь.

1 Ответ

9 голосов
/ 13 июля 2010

На каждой итерации цикла вы должны «захватывать» значение итерированного значения, прежде чем использовать его в замыкании. В противном случае Плагин в каждом делегате будет указывать на последнее значение Плагина вместо того значения, которое он имел при создании анонимной функции.

Более подробное объяснение Эрика Липперта вы можете прочитать здесь:

Закрытие переменной цикла, считающейся вредной - 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);
}
...