Почему события не могут использоваться одинаково в производных classах, как в базовом classе в C #?

В следующем коде я хочу расширить поведение classа, выведя / подclassифицируя его и использую событие базового classа:

public class A { public event EventHandler SomeEvent; public void someMethod() { if(SomeEvent != null) SomeEvent(this, someArgs); } } public class B : A { public void someOtherMethod() { if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible? //Error: The event 'SomeEvent' can only appear on the left hand side of += or -= //(except when used from within the type 'A') } } 

Почему это невозможно?

И каково общее решение для такого рода ситуаций?

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

Объяснение причин, по которым следует прочитать ответ Джона Скита или спецификацию C #, которая описывает, как компилятор автоматически создает частное поле.

Вот одна возможная работа.

 public class A { public event EventHandler SomeEvent; public void someMethod() { OnSomeEvent(); } protected void OnSomeEvent() { EventHandler handler = SomeEvent; if(handler != null) handler(this, someArgs); } } public class B : A { public void someOtherMethod() { OnSomeEvent(); } } 

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

Другие объяснили, как обойти эту проблему, но не почему она подходит.

Когда вы объявляете публичное полевое событие, компилятор создает публичное событие и частное поле. В пределах одного classа (или вложенных classов) вы можете получить непосредственно в поле, например, для вызова всех обработчиков. Из других classов вы видите только событие, которое разрешает подписку и отмену подписки.

Ответ Тодда верен. Часто вы увидите, что это реализовано на всей платформе .NET как OnXXX(EventArgs) :

 public class Foo { public event EventHandler Click; protected virtual void OnClick(EventArgs e) { var click = Click; if (click != null) click(this, e); } } 

Я настоятельно рекомендую вам рассмотреть шаблон EventArgs / EventHandler , прежде чем вы CustomEventArgs CustomEventHandler CustomEventArgs / CustomEventHandler для CustomEventHandler событий.

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

События в C # публично представлены с помощью пары методов add_SomeEvent и remove_SomeEvent , поэтому вы можете подписаться на событие вне classа, но не поднимать его.

Я бы ответил, что вам не нужно это делать.

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

Мой надуманный пример того, насколько бы отличается мир, если бы производным типам разрешалось поднимать события у своих предков. Примечание: это недопустимый код C # .. (все же ..)

 public class GoodVigilante { public event EventHandler LaunchMissiles; public void Evaluate() { Action a = DetermineCourseOfAction(); // method that evaluates every possible // non-violent solution before resorting to 'Unleashing the fury' if (null != a) { a.Do(); } else { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } } virtual protected string WhatsTheTime() { return DateTime.Now.ToString(); } .... } public class TriggerHappy : GoodVigilante { protected override string WhatsTheTime() { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } } // client code GoodVigilante a = new GoodVigilante(); a.LaunchMissiles += new EventHandler(FireAway); GoodVigilante b = new TriggerHappy(); // rogue/imposter b.LaunchMissiles += new EventHandler(FireAway); private void FireAway(object sender, EventArgs e) { // nuke 'em } 

Оберните его с помощью защищенного виртуального метода On …:

 public class BaseClass { public event EventHandler SomeEvent; protected virtual void OnSomeEvent() { if(SomeEvent!= null) SomeEvent(this, new MyArgs(...) ); } } 

Затем переопределите это в производном classе

 public class DerivedClass : BaseClass { protected override void OnSomeEvent() { //do something base.OnSomeEvent(); } } 

Вы установите этот шаблон на всем протяжении .Net – все формы и веб-элементы управления следуют за ним.

Не используйте префикс Raise … – это не согласуется с стандартами MS и может вызвать путаницу в другом месте.

  • Событие Internet Explorer и JavaScript currentTarget
  • Разница между e.target и e.currentTarget
  • Добавление и удаление анонимного обработчика событий
  • Когда используется пул streamов?
  • Событие Click не работает с динамически сгенерированными элементами
  • CKEDITOR - как добавить постоянное событие onclick?
  • Как проверить, была ли клавиша нажата клавишей со стрелкой в ​​Java KeyListener?
  • Остановить распространение событий мыши в угловом
  • Избегайте беды Invoke / BeginInvoke в обработке событий в WinForm для кросс-streamов?
  • Что означает логическое значение, возвращаемое из метода обработки событий в Android
  • Как можно удалить все обработчики событий события «Click» кнопки «Button»?
  • Interesting Posts

    Правильный / быстрый способ изменения таблицы данных.

    Команда не обнаружила ошибку в присвоении переменной Bash

    Дизайн – Где должны регистрироваться объекты при использовании Windsor

    iPhone «Закладка на Homescreen» удаляет cookies и сеанс?

    Нужно форматировать даты в динамически построенных WPF DataGrid

    CSS3 порядок преобразования имеет значение: самая правая операция сначала

    Вычисление сквозной задержки для SimpleServerApp в Veins-LTE

    C # – redirect на консоль в реальном времени

    Java 8 метод ссылки необработанное исключение

    Отказ Heroku отклонен, приложение, поддерживающее Cedar, не обнаружено

    Хорошие шаблоны для обработки ошибок VBA

    Как интерпретировать результаты команды `ls -l`?

    Получить все элементы управления определенного типа

    Создание Pandas DataFrame из массива Numpy: как указать столбцы индекса и заголовки столбцов?

    Hibernate – пакетное обновление возвращало неожиданный счетчик строк из обновления: 0 фактическое количество строк: 0 ожидаемых: 1

    Давайте будем гением компьютера.