Каков предпочтительный способ всплеска событий? - PullRequest
16 голосов
/ 03 декабря 2010

У меня есть три объекта. 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 ]

20 голосов
/ 03 декабря 2010

Другой подход, обернуть его, используя 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; }
    }
}
3 голосов
/ 03 декабря 2010

Вот как я это делаю. однако я бы порекомендовал изменить стреляющий механизм на этот, чтобы сделать его безопасным для потоков

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);
    }
}
1 голос
/ 03 декабря 2010

Как уже говорили другие ответы, это способ сделать это.

Но вы можете пойти дальше !!! Я только что реализовал на нем хорошую структуру данных, и это похоже на то, как вы это делаете.

Было бы неплохо иметь автоматическое всплытие событий? Вы можете реализовать это с помощью 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 .

Обратите внимание, что любая логика может быть реализована для привязки структурированных событий (вы можете быть вложенной регистрацией событий, используя имя и / или другие отраженные свойства.

...