Почему TEventArgs не стал противоречивым в стандартном шаблоне событий в экосистеме .NET? - PullRequest
0 голосов
/ 27 февраля 2019

Узнав больше о стандартной модели событий в .NET, я обнаружил, что перед введением обобщений в C # метод, который будет обрабатывать событие, представлен этим типом делегата:

//
// Summary:
//     Represents the method that will handle an event that has no event data.
//
// Parameters:
//   sender:
//     The source of the event.
//
//   e:
//     An object that contains no event data.
public delegate void EventHandler(object sender, EventArgs e);

Но после обобщенийбыли введены в C # 2, я думаю, что этот тип делегата был переписан с использованием обобщенности:

//
// Summary:
//     Represents the method that will handle an event when the event provides data.
//
// Parameters:
//   sender:
//     The source of the event.
//
//   e:
//     An object that contains the event data.
//
// Type parameters:
//   TEventArgs:
//     The type of the event data generated by the event.
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

У меня есть два вопроса здесь:

Во-первых, почему не было TEventArgs параметр типа сделал контравариантным ?

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

В книге Джозефа Албахари, C # в двух словах, я цитирую:

Если вы определяете общий тип делегата, рекомендуется:

  • ОтметитьПараметр типа, используемый только в возвращаемом значении как ковариантный (out).
  • Пометить любые параметры типа, используемые только в параметрах, как контравариантные (in).

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

Второй вопрос: Почему не было общего ограничения для принудительного применения TEventArgs к System.EventArgs ?

следующим образом:

public delegate void EventHandler<TEventArgs>  (object source, TEventArgs e) where TEventArgs : EventArgs; 

Заранее спасибо.

Отредактировано для пояснения второго вопроса:

Похоже, что общее ограничение для TEventArgs (, где TEventArgs: EventArgs ) было раньше, и онобыл удален Microsoft, так что команда разработчиков, похоже, поняла, что это не имеет особого практического смысла.

Я отредактировал свой ответ, включив в него некоторые скриншоты из

.NET referenceисточник

enter image description here

1 Ответ

0 голосов
/ 27 февраля 2019

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

Более того, вопросы «почему нет» этой формы, в которых задаются вопросы о мотивации и выборе людей, работающих в конкретной компании, могут быть подотчетны только тем людям, которые приняли это решение, которых, вероятно, здесь нет.

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

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

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

Это довольно слабая причина.Более сильная причина также является более грустной.

Как мы знаем, универсальные типы делегатов можно сделать ковариантными в их типах возвращаемых значений и контравариантными в их типах параметров;мы обычно думаем о дисперсии в контексте совместимости присваивания.То есть, если у нас есть Func<Mammal, Mammal> в руке, мы можем присвоить его переменной типа Func<Giraffe, Animal> и знать, что основная функция всегда будет забирать млекопитающего - потому что теперь он будет получать только жирафов - и всегда будетвернуть животное - потому что оно возвращает млекопитающих.

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

Полеподобные события реализуются с использованием суммирования делегатов;поэтому добавление обработчика к событию представляется как +=.(Я не большой поклонник этого синтаксиса, но мы застряли с ним сейчас.)

Хотя обе эти функции работают хорошо независимо друг от друга, они плохо работают в комбинации.Когда я реализовал дисперсию делегатов, наши тесты в короткие сроки обнаружили, что в CLR было несколько ошибок, связанных с добавлением делегатов, когда базовые типы делегатов не соответствовали из-за преобразований с поддержкой дисперсии.Эти ошибки существовали со времен CLR 2.0, но до C # 4.0 ни один основной язык не обнаруживал ошибок, не было написано ни одного теста для них и т. Д.

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

В то время мы работали с командой CLR, чтобы попытаться устранить эти ошибки для следующеговерсии CLR, но они не считались достаточно приоритетными по сравнению с их риском.Многие типы, такие как IEnumerable<T> и IComparable<T> и т. Д. Были сделаны вариантами в этих выпусках, как и типы Func и Action, но редко складывают два несовпадающих Func с использованиемвариант преобразования .Но для делегатов мероприятия их единственная цель в жизни - сложить вместе;они будут добавляться вместе все время, и если бы они были вариантами, был бы риск раскрытия этих ошибок большому количеству пользователей.

Вскоре после C # 4 я потерял учет проблем и, честно говоря, не знаю, были ли они когда-нибудь решены.Попробуйте сложить вместе несколько несовпадающих делегатов в различных комбинациях и посмотрите, не случится ли что-нибудь плохое!

Так что это хорошая, но неудачная причина, почему не делает вариант делегатов событий в период выпуска C # 4.0.Есть ли еще веская причина, я не знаю.Вы должны спросить кого-то из команды CLR.

...