Повышение событий базового класса в производных классах C # - PullRequest
8 голосов
/ 23 апреля 2009

У меня есть базовый класс 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» (событие, которое я получил при извлечении из формы), он говорит, что у меня не может быть Move на правой стороне без + = или - =. Я также удалил общие теги ShapeEventArgs.

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

Ответы [ 4 ]

12 голосов
/ 23 апреля 2009

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

Используйте base.OnMove () вместо.

4 голосов
/ 23 апреля 2009

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

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

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

3 голосов
/ 23 апреля 2009

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

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

1 голос
/ 23 апреля 2009

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

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

...