Почему события не поддерживают связывание унаследованных типов? - PullRequest
0 голосов
/ 23 марта 2011

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

EventHandler

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

FormClosingEventHandler

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

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

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

Ответы [ 2 ]

2 голосов
/ 23 марта 2011

Ну, это интересно ...

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

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

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

1 голос
/ 20 марта 2012

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

  1. Целевой объект, с которым должен работать метод, или `null` в случае статических методов
  2. Ссылка на функцию, которая действует на объект этого типа, или статическую функцию, если цель имеет значение «ноль».
  3. Тип самого делегата.

Делегат, созданный с использованием Delegate.Combine для объединения в общей сложности N однокомпонентных делегатов, будет содержать N целей, N методы и ONE тип делегата.Учитывая (имхо довольно странный и неудачный) способ использования 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>).

...