Создание событий базового класса в производных классах C#

У меня есть базовый класс DockedToolWindow : Form и множество классов, производных от DockedToolWindow. У меня есть класс-контейнер, который содержит и назначает события объектам DockedToolWindow, однако я хочу вызывать события из дочернего класса.

На самом деле у меня есть вопрос о том, как реализовать этот сайт MSDN говорит мне делать. Этот раздел ниже дает мне проблему:

    // The event. Note that by using the generic EventHandler<T> event type
    // we do not need to declare a separate delegate type.
    public event EventHandler<ShapeEventArgs> ShapeChanged;

    public abstract void Draw();

    //The event-invoking method that derived classes can override.
    protected virtual void OnShapeChanged(ShapeEventArgs e)
    {
        // Make a temporary copy of the event to avoid possibility of
        // a race condition if the last subscriber unsubscribes
        // immediately after the null check and before the event is raised.
        EventHandler<ShapeEventArgs> handler = ShapeChanged;
        if (handler != null)
        {
            handler(this, e);
        }
    }

Конечно, этот пример компилируется и работает, но когда я заменяю «ShapeChanged» на «Move» (событие, которое я получил от производного от Form), появляются ошибки, говорящие, что я не могу иметь Move с правой стороны без += или -=. Я также удалил общие теги ShapeEventArgs.

Любые подстрекательства о том, почему это не работает? В чем разница между событием, объявленным внутри класса, и событием, которое унаследовано?


person Game_Overture    schedule 23.04.2009    source источник


Ответы (4)


Вы не можете напрямую запускать события базового класса. Именно по этой причине вам пришлось сделать свой OnShapeChanged метод protected вместо private.

Вместо этого используйте base.OnMove(). .

person Groo    schedule 23.04.2009
comment
Я вижу, что OnMove() решит эту проблему, однако как не вызвать явный вызов OnMove(), когда это необходимо, не так ли? Я не знаю, где фреймворк на самом деле вызывает делегата Move, и, вероятно, он все равно скрыт от меня. - person Game_Overture; 25.04.2009
comment
OnMove запускает событие Move точно так же, как OnShapeChanged запускает событие ShapeChanged в вашем коде. Распространенным шаблоном является добавление защищенных членов, которые запускают события, чтобы сделать их видимыми для производных классов. В этом случае обычно добавляется префикс On (OnMove, OnClick и т. д.). - person Groo; 27.04.2009
comment
Чтобы увидеть, где платформа фактически запускает событие Move, используйте инструмент Reflector (red-gate.com /products/reflector), чтобы получить исходный код метода Form.OnMove. - person Groo; 27.04.2009
comment
[Примечание] С тех пор Reflector стал платным приложением, поэтому (если вы хотите получить бесплатное приложение) вам необходимо проверить ILSpy или другое подобное приложение. - person Groo; 06.02.2012
comment
JustDecompile от Telerik предоставляется бесплатно. - person Daniel; 17.07.2012
comment
До сих пор не понимаю, почему С# не позволяет просто запускать базовое событие из производного класса. Я могу понять, что не нужно менять участника события, стирать его и т. Д., А просто запускать его? - person stonedauwg; 05.02.2015

Из спецификации языка С#, раздел 10.7 (курсив добавлен):

В тексте программы класса или структуры, содержащей объявление события, определенные события могут использоваться как поля. Чтобы использоваться таким образом, событие не должно быть абстрактным или внешним и не должно явно включать объявления-аксессора-события. Такое событие можно использовать в любом контексте, который допускает поле. Поле содержит делегат (§15), который ссылается на список обработчиков событий, добавленных к событию. Если обработчики событий не были добавлены, поле содержит значение null.

Таким образом, причина, по которой вы не можете рассматривать событие Move как поле, заключается в том, что оно определено в другом типе (в данном случае в вашем суперклассе). Я согласен с предположением @womp о том, что дизайнеры сделали этот выбор, чтобы предотвратить непреднамеренное манипулирование событием. Кажется явно неправильным позволять несвязанным типам (типам, не производным от типа, объявляющего событие) делать это, но даже для производных типов это может быть нежелательно. Вероятно, им пришлось бы включить синтаксис, позволяющий сделать объявление события private или protected в отношении использования стиля поля, поэтому я предполагаю, что они решили просто полностью запретить его.

person Charlie    schedule 23.04.2009

Разница в размахе. Внутри вашего класса вы можете контролировать, как обрабатываются ваши делегаты событий, однако ваш класс не может контролировать то, что делает базовый класс. Он может делать какие-то сумасшедшие закулисные вещи с событием и его обработчиками. Если вы просто «переназначите» событие Move, вы уничтожите список делегатов многоадресной рассылки для этого события.

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

person womp    schedule 23.04.2009

Вам нужен только код, который вы разместили в классе, где определено само событие. Все производные классы должны просто вызывать OnShapeChanged() или OnMove() напрямую, без копирования и т. д., поэтому вам вообще не следует писать этот код в своих классах (поскольку событие Move определено в базе).

Если вам нужно выполнить какую-то обработку в производном классе (может быть, вам нужно повозиться с вашим классом коллекции?), вы переопределяете виртуальный вызов OnXXX и делаете все необходимое перед вызовом base.OnXXX(). В статье MSDN класс Circle соответствует вашему классу DockedToolWindow. Тот же шаблон должен быть доступен для ваших производных классов.

person David Pope    schedule 23.04.2009
comment
Я вижу, что OnMove() решит эту проблему, однако как не вызвать явный вызов OnMove(), когда это необходимо, не так ли? Я не знаю, где фреймворк на самом деле вызывает делегата Move, и, вероятно, он все равно скрыт от меня. - person Game_Overture; 25.04.2009