Ответ 1
Мой вопрос просто: какую часть С# Spec мне здесь не хватает?
Резюме:
- Вы обнаружили незначительную известную ошибку в реализации.
- Ошибка будет сохранена по соображениям обратной совместимости.
- Спецификация С# 3 содержала ошибку в отношении того, как должен обрабатываться "нулевой" случай; он был исправлен в спецификации С# 4.
- Вы можете воспроизвести поведение багги с помощью любой лямбда, где тип возврата не может быть выведен. Например:
Method(() => null);
Детали:
В спецификации С# 5 указано, что правило выдержки:
-
Если выражение имеет тип, выберите лучшее преобразование из этого типа в типы параметров-кандидата.
-
Если выражение не имеет тип и не является лямбдой, выберите преобразование в более подходящем типе.
-
Если выражение является лямбдой, тогда сначала рассмотрим, какой тип параметра лучше; если ни один из них не является лучшим, а типы делегатов имеют одинаковые списки параметров, тогда рассмотрим взаимосвязь между выведенным типом возвращаемого значения лямбда и возвращаемыми типами делегатов.
Итак, предполагаемое поведение: сначала компилятор должен проверить, является ли один тип параметра явно лучше, чем другой, независимо от того, имеет ли аргумент тип. Если это не решит ситуацию, а аргумент - лямбда, проверьте, какой тип возвращаемого возвращаемого значения преобразован в тип возвращаемых типов параметров делегатов.
Ошибка в реализации - это реализация не делает этого. Скорее, в случае, когда аргумент является лямбдой, он полностью пропускает проверку типа выдержки и переходит прямо к проверке проверки выводимого типа возвращаемого типа, которая затем терпит неудачу, потому что не существует выводимого типа возврата.
Я намерен исправить это для Рослина. Однако, когда я пошел на это, мы обнаружили, что создание исправления вызвало отказ от компиляции какого-либо реального кода. (Я не помню, что такое код реального мира, и у меня больше нет доступа к базе данных, которая содержит проблемы совместимости.) Поэтому мы решили сохранить существующую небольшую ошибку.
Я отмечаю, что ошибка была практически невозможна, прежде чем я добавил дисперсию делегата в С# 4; в С# 3 невозможно, чтобы два разных типа делегатов были более или менее конкретными, поэтому единственным правилом, которое могло бы применяться, было правило лямбда. Поскольку в С# 3 не было теста, который бы выявил ошибку, ее было легко написать. Мой плохой, извините.
Я также отмечаю, что, когда вы начинаете бросать типы дерева выражений в микс, анализ становится еще более сложным. Даже если Func<string>
лучше, чем Func<object>
, Expression<Func<string>>
не конвертируется в Expression<Func<object>>
! Было бы неплохо, если бы алгоритм для betterness был агностиком в отношении того, лямбда направлялась к дереву выражений или делегату, но в некотором смысле это не так. Эти случаи осложняются, и я не хочу здесь трудиться.
Эта небольшая ошибка - это предметный урок в важности реализации того, что фактически говорит спецификация, а не того, что, по вашему мнению, она говорит. Если бы я был более осторожен в С# 3, чтобы убедиться, что код соответствует спецификации, тогда код будет сбой в случае с "нулевым", и тогда было бы ясно, что спецификация С# 3 была неправильной. И реализация делает проверку лямбды перед проверкой типа, которая была бомбой замедленного действия, ожидающей выхода, когда С# 4 скатывается, и вдруг это стало неправильным кодом. Проверка типа должна была быть сделана в первую очередь независимо.