Позвольте мне немного прояснить ваш вопрос.
Эти три вещи могут быть преобразованы в тип делегата: (1) лямбда (или анонимный метод в стиле C # 2), (2) группа методовили локальный метод, (3) другой делегат.Различны ли правила для того, какие ковариантные и контравариантные преобразования являются правовыми в каждом случае?
Да.
Чем они отличаются?
Вы должны прочитать спецификацию для точных деталей, но кратко:
Универсальный тип делегата может быть преобразован в другой универсальный тип делегата , только если параметры типа делегата помечены как ковариантные.или контравариантный .То есть Func<Giraffe>
можно преобразовать в Func<Animal>
, поскольку Func<out T>
помечен как ковариантный.(Кроме того: если вам нужно выполнить вариантное преобразование из одного типа делегата в другой, и тип делегата не поддерживает дисперсию, вместо этого вы можете использовать группу методов метода Invoke
делегата «source», итеперь мы используем правила группы методов, но теряем равенство ссылок.)
Группу методов или локальный метод можно преобразовать в соответствующий тип делегата, используя правила ковариации и контравариантности, даже еслиделегат не помечен как поддерживающая дисперсия.То есть вы можете конвертировать Giraffe G()
в delegate Animal D();
, даже если D
не является универсальным, или является универсальным, но не помечен как вариант.
Правила преобразования лямбд сложны,Если лямбда не имеет формальных типов параметров, то используются формальные типы параметров целевого типа , тело лямбда анализируется, и оно может быть преобразовано, если тело анализирует без ошибок и результат совместим стип результата целевого типа. Если у лямбды есть формальные типы параметров, они должны точно соответствовать формальным типам параметров целевого типа .
Чем они отличаются?
Разные вещи разные.Я действительно не знаю, как ответить на такой смутный, широкий вопрос «почему».
Эти правила были получены дюжиной людей, сидевших в комнате в течение многих лет.Группа методов для делегирования преобразований была добавлена в C # 1, общие делегаты были добавлены в C # 2, лямбда-выражения были добавлены в C # 3, универсальная дисперсия делегата была добавлена в C # 4. Я понятия не имею, как ответить на вопрос «почему» обуквально сотни часов работы по дизайну, которые были выполнены, и более половины из них были до того, как я был в команде дизайнеров.Эта проектная работа включала множество аргументов и компромиссов. Пожалуйста, не задавайте смутные вопросы "почему" и "почему нет" о дизайне языка программирования .
Вопросы типа "какая страница спецификации определяет это поведение?"есть ответ, но "почему спецификация говорит это?"в основном просит психологический анализ людей, которые занимались этим дизайном пятнадцать лет назад, и почему они нашли определенные компромиссы неотразимыми, а другие не так уж и много.Я не способен или не желаю делать этот анализ;это потребовало бы перефразирования буквально сотен часов аргументов.
Если ваш вопрос «каковы общие принципы построения языка, которые поощряют или не поощряют точное или неточное соответствие?»это тема, которую я мог бы обсудить в течение нескольких часов.Например, я разработал новый алгоритм разрешения перегрузки вчера , а разрешение перегрузки составляет около не что иное, как решение, когда важны точные или неточные совпадения и насколько они важны . Задайте более конкретный вопрос .
Скажи, что, давай ты сделаешь эту работу вместо меня.Вот один из ваших сценариев:
Action<Mammal> ma = (Animal a) => ...
Опишите мне убедительную выгоду от запрета пользователю писать эту строку кода. Пример: для меня это выглядит как ошибка.Похоже, пользователь начал печатать одну вещь и передумал на полпути.Такая бессмысленная причудливая несогласованность очень характерна для неаккуратного, глючного кода, и ее легко можно предотвратить. Один из принципов разработки C # заключается в том, что язык говорит вам, когда вы, вероятно, допустили ошибку .Это наверняка выглядит как ошибка.
Теперь сделайте контраргумент, что код должен быть разрешен .Пример: Является ли также общим принципом, что должна быть согласованность между лямбдами и локальными методами в отношении их правил конвертируемости?Насколько важно это правило по сравнению с правилом о предотвращении небрежных ошибок?
Теперь придумайте еще дюжину аргументов о тонких плюсах и минусах каждого выбора и о том, как различные сценарии разработки влияют на ваш анализкаждый выбор.Приведите множество примеров реалистичного кода.
Имейте в виду, что некоторые пользователи являются экспертами в системах типов, а некоторые нет.Некоторые из них архитекторы с двадцатилетним опытом работы, некоторые только что закончили колледж.Некоторые из них являются программистами на Java, которые только что взялись за C # вчера и все еще находятся в состоянии стирания;некоторые - программисты F #, которые привыкли к выводу полной программы. Сделайте подробные заметки о плюсах и минусах каждого сценария, а затем предложите компромиссное предложение, которое не слишком компрометирует ни один важный сценарий.
Теперь рассмотрим затраты.Сложно ли будет реализовать предложенную функцию?Это добавляет новое сообщение об ошибке?Понятно ли сообщение, или оно запутает пользователей?Предотвращает ли предложенная функция какую-либо будущую функцию?Я отмечаю, что для этого шага нужно сделать хороших прогнозов будущего языка.
Как только вы примете решение, тогда опишите всю эту работув одном предложении, которое отвечает на вопрос «почему вы решили это?»