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

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

EventHandler

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

FormClosingEventHandler

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

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

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


person Homam    schedule 23.03.2011    source источник


Ответы (2)


arrow_upward
3
arrow_downward

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

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

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 недоступно. Не похоже, что одно наследуется от другого.

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

person Jon Skeet    schedule 23.03.2011
comment
Действительно отличный ответ, спасибо. Единственная проблема заключается в том, что среда IDE, которая генерирует обработчики событий в пользовательском интерфейсе, не поддерживает такую ​​рабочую среду. поэтому мне приходится связывать их вручную. Для этого я спросил логически, почему он не поддерживается. - person Homam; 23.03.2011
comment
@John: Ба, я ненавижу Visual Studio, когда дело доходит до обработчиков событий. Надеюсь, ответ объясняет, почему существуют ограничения, когда речь идет о реальном коде - ограничения VS выходят за рамки моего понимания;) - person Jon Skeet; 23.03.2011

arrow_upward
1
arrow_downward

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

  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>).

person supercat    schedule 19.03.2012