Ответ 1
Прежде всего, чтобы ответить на некоторые проблемы в комментариях к вопросу: я, как правило, настойчиво отвечаю на вопросы "почему нет", потому что трудно найти краткие причины, почему все в мире решили не делать эту работу, и потому что вся работа не сделано по умолчанию. Скорее, вам нужно найти причину для выполнения работы и отнять ресурсы у другой работы, которая менее важна для ее выполнения.
Более того, вопросы "почему нет" этой формы, в которых задаются вопросы о мотивации и выборе людей, работающих в конкретной компании, могут быть подотчетны только тем людям, которые приняли это решение, которых, вероятно, здесь нет.
Однако в этом случае мы можем сделать исключение из моего общего правила закрытия вопросов "почему нет", потому что этот вопрос иллюстрирует важный момент о ковариации делегатов, о котором я никогда не писал ранее.
Я не принял решение оставить делегатов событий неизменными, но если бы я был в состоянии сделать это, я бы оставил делегатов событий неизменными по двум причинам.
Первый - это просто пункт "поощрять передовой опыт". Обработчики событий обычно специально создаются для обработки определенного события, и я не знаю, почему я знаю, что проще сделать это проще, чем использовать делегаты, у которых есть несоответствия в сигнатуре, в качестве обработчиков, даже если эти несоответствия могут быть иметь дело с помощью дисперсии. Обработчик событий, который точно соответствует во всех отношениях событию, которое он должен обрабатывать, дает мне больше уверенности в том, что разработчик знает, что он делает, создавая управляемый событиями рабочий процесс.
Это довольно слабая причина. Более сильная причина также является более печальной.
Как мы знаем, универсальные типы делегатов могут быть сделаны ковариантными в своих типах возвращаемых данных и контравариантными в их типах параметров; мы обычно думаем о дисперсии в контексте совместимости присваивания. То есть, если у нас есть Func<Mammal, Mammal>
в руках, мы можем присвоить его переменной типа Func<Giraffe, Animal>
и знать, что основная функция всегда будет брать млекопитающее - потому что теперь он получит только жирафы - и всегда будут возвращать животное - потому что оно возвращает млекопитающих.
Но мы также знаем, что делегаты могут быть добавлены вместе; делегаты являются неизменяемыми, поэтому при добавлении двух делегатов вместе получается третий; сумма является последовательным составом слагаемых.
Полевые события реализуются с использованием суммирования делегатов; поэтому добавление обработчика к событию представляется как +=
. (Я не большой поклонник этого синтаксиса, но мы застряли с ним сейчас.)
Хотя обе эти функции работают хорошо независимо друг от друга, они плохо работают в комбинации. Когда я реализовал дисперсию делегатов, наши тесты в короткие сроки обнаружили, что в CLR есть ряд ошибок, связанных с добавлением делегатов, когда базовые типы делегатов не совпадали из-за преобразований с поддержкой дисперсии. Эти ошибки существовали со времен CLR 2.0, но до С# 4.0 ни один основной язык не обнаруживал ошибок, не было написано ни одного теста для них и так далее.
К сожалению, я не помню, какие были репродукторы для ошибок; это было двенадцать лет назад, и я не знаю, есть ли еще какие-нибудь заметки на нем, спрятанные где-нибудь на диске.
В то время мы работали с командой CLR, чтобы попытаться устранить эти ошибки для следующей версии CLR, но они не считались достаточно приоритетными по сравнению с их риском. Многие типы, такие как IEnumerable<T>
и IComparable<T>
и т.д., Были сделаны вариантами в этих выпусках, как и типы Func
и Action
, но редко суммируются два несовпадающих Func
с использованием преобразования вариантов. Но для делегатов мероприятия их единственная цель в жизни - сложить вместе; они будут добавляться вместе все время, и если бы они были вариантами, был бы риск раскрытия этих ошибок большому количеству пользователей.
Вскоре после С# 4 я потерял учет проблем и, честно говоря, не знаю, были ли они когда-нибудь решены. Попробуйте сложить несколько несовпадающих делегатов в различных комбинациях и посмотрите, не случится ли что-нибудь плохое!
Так что это хорошая, но прискорбная причина, по которой нельзя делать вариант делегатов событий в период выпуска С# 4.0. Есть ли еще веская причина, я не знаю. Вы должны спросить кого-то из команды CLR.