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

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

EventHandler

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

FormClosingEventHandler

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

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

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


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


Ответы (2)


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

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

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

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

  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