Каков предпочтительный способ пузырей событий?
У меня есть три объекта 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 вообще, только что он поднял событие.
Ответы
Ответ 1
Другой подход - обернуть его, используя add/remove:
public class ObjectB
{
ObjectC objC;
public ObjectB()
{
objC = new ObjectC();
}
public event EventFiredEventHandler EventFired
{
add { this.objC.EventFired += value; }
remove { this.objC.EventFired -= value; }
}
}
Ответ 2
То, как я это делаю. однако я бы рекомендовал изменить ваш механизм обстрела на это, чтобы сделать его потокобезопасным
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);
}
}
Я ошибался в отношении шаблона threadafe, вот полный шаблон событий threadafe
/// <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);
}
}
Ответ 3
Как утверждают другие ответы, это способ сделать это.
Но вы можете пойти дальше!!! Я только что внедрил на нем хорошую структуру данных, и мне нравится нажимать на нее.
Было бы хорошо иметь автоматическое событие пузыря? Вы можете реализовать его с помощью 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.
Обратите внимание, что любая логика может быть реализована для связывания структурированных событий (вы можете быть вложенной регистрацией событий с использованием имени и/или других отраженных свойств.