Каков предпочтительный способ всплеска событий?
У меня есть три объекта ObjectA имеет ObjectB, ObjectB имеет ObjectC. Когда ObjectC запускает событие, мне нужно, чтобы ObjectA знала об этом, так что это то, что я сделал...
public delegate void EventFiredEventHandler();
public class ObjectA
{
ObjectB objB;
public ObjectA()
{
objB = new ObjectB();
objB.EventFired += new EventFiredEventHandler(objB_EventFired);
}
private void objB_EventFired()
{
//Handle the event.
}
}
public class ObjectB
{
ObjectC objC;
public ObjectB()
{
objC = new ObjectC();
objC.EventFired += new EventFiredEventHandler(objC_EventFired);
objC.FireEvent();
}
public event EventFiredEventHandler EventFired;
protected void OnEventFired()
{
if(EventFired != null)
{
EventFired();
}
}
private void objC_EventFired()
{
//objC fired an event, bubble it up.
OnEventFired();
}
}
public class ObjectC
{
public ObjectC(){}
public void FireEvent()
{
OnEventFired();
}
public event EventFiredEventHandler EventFired;
protected void OnEventFired()
{
if(EventFired != null)
{
EventFired();
}
}
}
Это правильный способ справиться с этим, или есть лучший способ? Я не хочу, чтобы ObjectA вообще знала об ObjectC, только то, что она вызвала событие.
3 ответа
Другой подход заключается в том, чтобы обернуть его с помощью добавления / удаления:
public class ObjectB
{
ObjectC objC;
public ObjectB()
{
objC = new ObjectC();
}
public event EventFiredEventHandler EventFired
{
add { this.objC.EventFired += value; }
remove { this.objC.EventFired -= value; }
}
}
Вот так я и делаю. однако я бы порекомендовал изменить ваш стреляющий механизм на этот, чтобы сделать его безопасным для потока
protected void OnEventFired()
{
var tmpEvent = EventFired;
if(tmpEvent != null)
{
tmpEvent();
}
}
Это удерживает его от сбоя, если EventFired становится нулевым между нулевой проверкой и срабатыванием.
Также несколько стандартно следовать шаблону EventHandler для ваших делегатов.
protected virtual void OnEventFired(EventArgs e)
{
var tmpEvent = EventFired;
if(tmpEvent != null)
{
tmpEvent(this, EventArgs.e);
}
}
Я ошибся насчет паттерна безопасных потоков, вот полный паттерн безопасных потоков
/// <summary>
/// Delegate backing the SomeEvent event.
/// </summary>
SomeEventHandler someEvent;
/// <summary>
/// Lock for SomeEvent delegate access.
/// </summary>
readonly object someEventLock = new object();
/// <summary>
/// Description for the event
/// </summary>
public event SomeEventHandler SomeEvent
{
add
{
lock (someEventLock)
{
someEvent += value;
}
}
remove
{
lock (someEventLock)
{
someEvent -= value;
}
}
}
/// <summary>
/// Raises the SomeEvent event
/// </summary>
protected virtual OnSomeEvent(EventArgs e)
{
SomeEventHandler handler;
lock (someEventLock)
{
handler = someEvent;
}
if (handler != null)
{
handler (this, e);
}
}
Как утверждают другие ответы, это способ сделать это.
Но вы можете пойти дальше!!! Я только что реализовал на нем хорошую структуру данных, и это похоже на то, как это можно сделать.
Было бы неплохо иметь автоматическое всплытие событий? Вы можете реализовать это с помощью Reflection. Мой способ состоит в том, чтобы определить интерфейс / базовый класс, который объявляет событие (или набор событий). Затем конструктор без параметров базового класса будет выполнять итерации других своих свойств / полей и автоматически регистрировать события членов для распространения событий.
Есть некоторые ограничения на дизайн, но если у вас есть глубокая структура и / или много (структурированных) событий, было бы хорошо, чтобы все было настроено без какой-либо дополнительной строки кода.
Начальный базовый класс может быть:
class BaseObject {
public BaseObject() {
FieldInfo[] fInfos = this.GetType().GetFields(...);
foreach (FieldInfo fInfo in fInfos) {
object fInfoValue = fInfo.GetValue(this, null);
if (fInfoValue is BaseObject) {
BaseObject bMemberObject = (BaseObject)fInfoValue;
bMemberObject.MyEvent += new EventHandler(delegate() {
if (this.MyEvent != null)
MyEvent();
});
}
}
public event MyEvent = null;
}
Конечно, как уже предлагалось, следуйте делегату делегата события (отправитель объекта, аргументы EventArgs) (для ясности я использовал более простое событие). Естественно, подразумевается, что ваши классы A, B и C являются производными непосредственно от BaseObject.
Обратите внимание, что любая логика может быть реализована для привязки структурированных событий (вы можете быть вложенной регистрацией событий, используя имя и / или другие отраженные свойства.