Обработка событий с помощью методов расширения C#

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

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

public static class EventHandlerExtensions
{
    public static void Raise<TEventArgs>(
        this EventHandler<TEventArgs> eventHandler, 
        object sender, TEventArgs args)  where TEventArgs:EventArgs
    {
        if (eventHandler != null)
        {
            eventHandler(sender, args);
        }
    }
}

public class Test
{
    private event EventHandler<EventArgs> EventA;
    private event EventHandler<EventArgs> EventB;

    public Test()
    {
        Console.WriteLine("::Start");
        EventB += EventA.Raise;
        EventA += (s, a) => Console.WriteLine("Event A raised");
        EventB.Raise(this, EventArgs.Empty);
        Console.WriteLine("::End");
    }
}

В этом примере EventA должна запускаться в результате Запуск EventB. Однако, когда я запускаю этот код, EventB срабатывает, но метод расширения на A не находит для него прослушивателей.

Если я изменю порядок вокруг, все работает нормально:

Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");

кроме того, вызов EventA.Поднять из лямбды отлично работает:

Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");

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

какие идеи?

2 ответов


вы захватываете старое значение EventA в закрытие своей функцией Raise. Поскольку позже вы используете += он изменяет значение EventA, но ваше закрытие все еще имеет старое значение.

код:

EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised"); 

может быть расширен в эквивалентный код, который дает понять, почему вы получаете старый делегат:

var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value 
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)

вы можете добавить следующее до EventB += EventA.Raise чтобы убедиться, что код действительно вызывает старое событие для:

EventA += (s, a) => Console.WriteLine("Old Event A raised");

объекты делегата неизменяемые. Как струны. Поэтому, когда вы назначаете EventA, вы создаете new