Добавление одного обработчика событий в другой - PullRequest
2 голосов
/ 26 июня 2009

У меня есть класс, который оборачивает другой класс и выставляет несколько событий из класса, который он оборачивает. (Экземпляр, который он упаковывает, может измениться)

Я использовал следующий код:

public event EventHandler AnEvent;

public OtherClass Inner {
    get { /* ... */ }
    set {
        //...
        if(value != null)
            value.AnEvent += AnEvent;
        //...
    }
}

Однако события возникли непоследовательно.

Что не так с этим кодом?

Ответы [ 2 ]

3 голосов
/ 26 июня 2009

ваш ответ - здесь две проблемы ...

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

Это особенно важно в таких вещах, как элементы управления winform или реализации списка привязок, где sender используется для идентификации объекта между многими, которые имеют общий обработчик.

Вместо этого это должно быть что-то вроде:

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent;
    if (handler != null) handler(this, e);
}

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

private EventHandler anEvent;
public event EventHandler AnEvent {
    add { // note: not synchronized
        bool first = anEvent == null;
        anEvent += value;
        if(first && anEvent != null && inner != null) {
            inner.SomeEvent += Inner_AnEvent;
        }
    }
    remove { // note: not synchronized
        bool hadValue = anEvent != null;
        anEvent -= value;
        if(hadValue && anEvent == null && inner != null) {
            inner.SomeEvent -= Inner_AnEvent;
        }
    }
}

(и аналогичный код во внутреннем get / set, чтобы подписаться, только если у нас есть слушатели ...

if(value != null && anEvent != null)
    value.AnEvent += Inner_AnEvent;

Это может сильно помочь, если у вас много экземпляров внешнего класса, но вы редко используете событие.

3 голосов
/ 26 июня 2009

Проблема в том, что Delegate s являются неизменными.

Если вы добавляете обработчик к событию, он создает новый экземпляр Delegate, который содержит старые обработчики и вновь добавленный обработчик. Старый Delegate не изменяется и отбрасывается.

Когда я пишу, value.AnEvent += AnEvent, он добавляет Delegate, содержащий текущие обработчики (если есть), к событию внутреннего класса. Однако изменения в событии внешнего класса игнорируются, потому что они не изменяют экземпляр Delegate, который я добавил в событие внутреннего класса. Аналогично, если я удаляю обработчик после установки свойства Inner, обработчик не удаляется из события внутреннего класса.


Есть два правильных способа сделать это.

Я могу сделать свой собственный обработчик, который вызывает событие оболочки, например:

public event EventHandler AnEvent;

public OtherClass Inner {
    get { /* ... */ }
    set {
        if(Inner != null)
            Inner.AnEvent -= Inner_AnEvent;

        //...

        if(value != null)
            value.AnEvent += Inner_AnEvent;

        //...
    }
}

void Inner_AnEvent(object sender, EventArgs e) { 
    var handler = AnEvent;
    if (handler != null) handler(sender, e);
}

Другой способ - создать пользовательское событие в оболочке, которое добавляет его обработчики к событию внутреннего класса, например:

EventHandler anEventDelegates

public OtherClass Inner {
    get { /* ... */ }
    set {
        //...
        if(value != null)
            value.AnEvent += anEventDelegates;
        //...
    }
}
public event EventHandler AnEvent {
    add {
        anEventDelegates += value;
        if (Inner != null) Inner.AnEvent += value;
    }
    remove {
        anEventDelegates -= value;
        if(Inner != null) Inner -= value;
    }
}

Обратите внимание, что это не совсем потокобезопасно.

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

...