Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Почему события не поддерживают привязку унаследованных типов?

Автор вопроса: HomamОтветов: 2

У этих делегатов:

EventHandler

публичный делегат void EventHandler (отправитель объекта, EventArgs e);

FormClosingEventHandler

публичный делегат void FormClosingEventHandler (отправитель объекта, FormClosingEventArgs e);

FormClosingEventArgs унаследован от EventArgs. Почему я не могу связать событие FormClosing с обработчиком события от делегата EventHandler?

Я знаю, что подпись обработчика события должна соответствовать его делегату, но почему он не поддерживает сопоставление унаследованных типов?

eventsdelegatesevent-handling

Ответы

Ответ 1 · рейтинг 3 · Jon Skeet

Что ж, интересно ...

Вы можете связать обработчик событий с помощью преобразования группы методов с совместимыми типами:

public void GenericHandlerMethod(object sender, EventArgs e) {}

...
// Valid
foo.FormClosingEvent += GenericHandlerMethod;

Это фактически создаст экземпляр FormClosingEventHandler, не EventHandler.

Однако вы не можете подписаться напрямую с существующим делегатом типа EventHandler:

EventHandler genericHandler = GenericHandlerMethod;
// Invalid
foo.FormClosingEvent += genericHandler;

... но вы можете создать новый делегат на основе существующего, если типы совместимы:

EventHandler generic = GenericHandlerMethod;
FormClosingEventHandler closingHandler = new FormClosingEventHandler(generic);
// Valid
foo.FormClosingEvent += closingHandler;

По сути, вам нужно помнить, что весь синтаксический сахар - это эффективно вызов такого метода:

foo.AddFormClosingHandler(handler);

где метод имеет подпись:

public void AddFormClosingHandler(FormClosingHandler handler)

Теперь помните, что, хотя у них есть совместимые подписи, преобразование ссылок из EventHandler в FormClosingHandler недоступно. Не похоже, что одно наследуется от другого.

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

Ответ 2 · рейтинг 1 · supercat

Делегат, который указывает непосредственно на метод объекта, содержит три части информации:

  1. The target object upon which the method should act, or `null` in the case of static methods
  2. A reference to a function which acts upon that type of object, or a static function if the target is `null`.
  3. The type of the delegate itself.

Делегат, который создается с использованием Delegate.Combine для объединения всего N делегатов с однократным преобразованием, будет содержать N целей, N методов и ОДИН тип делегата. Учитывая (ИМХО, довольно неприятный и неудачный) способ использования Delegate.Combine и Delegate.Remove, система никак не может разрешить существующее использование Combine для приема делегатов разных типов.

Например, для подпрограммы может потребоваться EventHandler<IFoo>. Если классы Moe и Larry реализуют IFoo и IBar, такая процедура должна иметь возможность принимать EventHandler<Moe> или EventHandler<Larry>. Если бы у каждого типа делегата было собственное определение для Combine, можно было бы передать EventHandler<IFoo>.Combine() делегат типа EventHandler<Moe> и один из EventHandler<Larry> и заставить его сгенерировать комбинированный обработчик делегата типа EventHandler<IFoo>. К сожалению, существует один Delegate.Combine() метод для всех типов делегатов, и он не может взглянуть на EventHandler<Moe> и EventHandler<Larry> и выяснить, какого типа должен быть объединенный делегат (даже если Delegate.Combine имел возможность определять типы, для которых оба обработчика событий может быть приведен, у него не будет возможности узнать, использовать ли EventHandler<IFoo> или EventHandler<IBar>).

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы