Почему события не могут использоваться одинаково в производных 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') } }
Почему это невозможно?
И каково общее решение для такого рода ситуаций?
- Каковы различия между делегатами и событиями?
- ga или _gaq.push для отслеживания событий Google Analytics?
- Как создать 5 кнопок и назначить отдельные события кликов динамически?
- Неактивность и активность WPF
- Отменить синхронизированное событие в Swift?
- Делегирование: EventEmitter или Наблюдаемое в Угловом
- Как подписаться на события другого classа в c #?
- Получить отредактированный TreeNode от CellEditorListener
- Как получить событие click ONLY только для родительского DIV, а не для детей?
- выбрать событие выбора jquery select
- Как я могу отменить регистрацию «анонимного» обработчика событий
- Отслеживание положения прокрутки и уведомление об этом других компонентах
- Как события вызывают утечку памяти на C # и как облегчить работу слабых ссылок?
Стандартная практика здесь заключается в том, чтобы иметь защищенный виртуальный метод 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 и может вызвать путаницу в другом месте.