Проблема с событием "сцепление" - PullRequest
6 голосов
/ 07 января 2010

Примечание: я редактировал этот вопрос, чтобы другим людям с такой же проблемой было легче получить помощь здесь. Чтобы увидеть оригинальный вопрос, который лучше подходит для некоторых ответов, проверьте историю изменений.

В проекте у меня есть класс ExecutionManager, который может содержать несколько экземпляров ExecutionSlot. Класс ExecutionSlot имеет несколько открытых полей событий, таких как:

public event EventHandlers.ObjectEventHandler<IPlugin> ExecuteCompleted;

Для каждого из этих событий в ExecutionManager есть соответствующее событие. Желаемое поведение заключается в том, что каждый раз, когда на ExecutionSlot возникает событие, соответствующее событие также вызывается на содержащем ExecutionManager.

Внедренное решение состояло в том, что всякий раз, когда в ExecutionManager был добавлен слот ExecutionSanager, ExectionManager добавлял свои собственные события в слот ExecutionSlot следующим образом:

executionSlot.ExecuteCompleted += ExecuteCompleted;

Нет необходимости удалять слот ExecutionSlot, поэтому события также никогда не удаляются.

Проблема в том, что событие в ExecutionManager не вызывается. После подтверждения того, что событие выполнялось слотом ExecutionSlot, я обнаружил, что изменение вышеуказанной строки на следующее решило проблему:

executionSlot.ExecuteCompleted += (sender, eventArgs) => ExecuteCompleted(sender, eventArgs);

И я не мог понять, почему, поэтому мой вопрос заключался в том, в чем разница.

Причиной такого различия было то, что первый добавляет текущих слушателей события ExecutionManager к событию ExecutionSlot. Поэтому любые слушатели, добавленные позже, не будут вызываться при возникновении события. Напротив, последнее решение использует лямбду для вызова события ExecutionManager, что означает, что будут вызываться прослушиватели во время события.

Основная причина неудачного первого решения заключается в том, что делегаты являются неизменяемыми. Поэтому, когда вы добавляете нового делегата в событие, вы фактически создаете новый делегат, который содержит существующие делегаты и добавленные. Поэтому любая ссылка на делегатов, сделанная ранее, не будет содержать вновь добавленного делегата.

Ответы [ 4 ]

4 голосов
/ 07 января 2010

РЕДАКТИРОВАТЬ: Этот ответ предполагал, что ExecuteCompleted был метод . На самом деле это поле , которое полностью меняет дело. Я оставлю этот ответ здесь ради потомков.

Первая версия добавляет обработчик событий с делегатом, созданным из автоматически сгенерированного метода, который, в свою очередь, просто вызывает ExecuteCompleted. Это немного похоже на это:

private void <>AutogeneratedMethodWithUnspeakableName(object sender, EventArgs e)
{
    ExecuteCompleted(e);
}
...
executionSlot.ExecuteCompleted += <>AutogeneratedMethodWithUnspeakableName;

Вторая версия добавляет обработчик событий с делегатом, созданным непосредственно из метода ExecuteCompleted.

В основном, первая форма - это дополнительный уровень перенаправления. Обычно это не имеет никакого значения, за исключением отказа от подписки, как упомянул JoelFan. Я бы угадал в этом проблема.

Класс, вызывающий событие , может отражать поверх подключенных обработчиков и смотреть на имена методов, реагируя по-разному в данном конкретном случае - но это очень маловероятно.

4 голосов
/ 07 января 2010

Одна идея ... возможно, в вашем коде есть место, где вы делаете:

executionSlot.ExecuteCompleted -= ExecuteCompleted;

, который отменяет подписку на событие, если вы используете исходный синтаксис подписки, но не удаляет его после внесения изменений.

3 голосов
/ 07 января 2010

посмотрите на этот другой пост на stackoverflow

b += (s, e) => a(s, e);

не совпадает с

b += a;

Добавляет текущее содержимое a к b, так что, если позднее число обработчиков зачисляется с a, это не вызовет их при вызове b.

0 голосов
/ 07 января 2010

Я считаю, что здесь происходит то, что в первом примере создается некий временный объект с пустым обработчиком событий, который вызывается, но ничего не делает.

Второй пример, который, как вы говорите, работает, - это обработчик событий вашего объекта с реальным кодом. Хотя я не совсем уверен, что там происходит, но это мое лучшее предположение.

Конечно, первый пример плохо пахнет в любом случае, поскольку он использует лямбда-выражения, чтобы запутать смысл без реальной добавленной стоимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...