Ответ 1
ОК, здесь сделка.
Краткая версия:
- Ошибка двусмысленности, как ни странно, правильная.
- Компилятор С# 4 также создает ложную ошибку после правильной ошибки неоднозначности. Это похоже на ошибку в компиляторе.
Длинная версия:
У нас проблема с разрешением перегрузки. Разрешение перегрузки очень хорошо определено.
Шаг первый: определите набор кандидатов. Это просто. Кандидатами являются Foo(Func<IEnumerable<String>>)
и Foo(Func<String>)
.
Шаг второй: определите, какие члены набора кандидатов применимы. Применимый член имеет каждый аргумент, конвертируемый в каждый тип параметра.
Является ли Foo(Func<IEnumerable<String>>)
применимым? Хорошо, X
конвертируется в Func<IEnumerable<String>
?
Проконсультируем раздел 6.6 спецификации. Эта часть спецификации - это то, что мы, разработчики языка, называем "действительно странным". В принципе, в нем говорится, что преобразование может существовать, но использование этого преобразования является ошибкой. (Есть веские причины, по которым мы сталкиваемся с этой странной ситуацией, в основном связанной с тем, чтобы избежать будущих изменений и избежать ситуаций "курица и яйцо", но в вашем случае мы получаем несколько неудачное поведение в результате.)
В принципе, здесь правило состоит в том, что преобразование из X в тип делегата без параметров существует, если разрешение перегрузки при вызове формы X()
будет успешным. Очевидно, что такой вызов будет успешным, и поэтому существует конверсия. Фактически использование этого преобразования является ошибкой, потому что типы возврата не совпадают, но разрешение перегрузки всегда игнорирует типы возвращаемых данных.
Итак, преобразование существует от X
до Func<IEnumerable<String>
, и поэтому перегрузка является применимым кандидатом.
Очевидно, по той же причине другая перегрузка также является применимым кандидатом.
Шаг третий: теперь у нас есть два подходящих кандидата. Какой из них "лучше"?
Тот, который является "лучшим", является тем, у кого более конкретный тип. Если у вас есть два подходящих кандидата, M(Animal)
и M(Giraffe)
, мы выбираем версию Жирафа, потому что Жираф более специфичен, чем Животное. Мы знаем, что Жираф более конкретен, потому что каждый Жираф - это Животное, но не каждое Животное - Жираф.
Но в вашем случае ни один тип не является более конкретным, чем другой. Нет никакого преобразования между двумя типами Func.
Поэтому ни то, ни другое не лучше, поэтому ошибка перегрузки сообщает об ошибке.
Затем компилятор С# 4 имеет то, что кажется ошибкой, где его режим восстановления ошибок в любом случае выбирает одного из кандидатов и сообщает о другой ошибке. Мне непонятно, почему это происходит. В основном это говорит о том, что восстановление ошибок выбирает перегрузку IEnumerable, а затем замечает, что преобразование группы методов приводит к несостоятельному результату; а именно, что строка не совместима с IEnumerable<String>
.
Вся ситуация довольно неудачна; возможно, было бы лучше сказать, что преобразование метода-группы-делегирования отсутствует, если типы возврата не совпадают. (Или, что преобразование, которое производит ошибку, всегда хуже, чем конверсия, которая этого не делает.) Однако мы застряли с этим сейчас.
Интересный факт: правила преобразования для lambdas учитывают возвращаемые типы. Если вы скажете Foo(()=>X())
, тогда мы поступим правильно. Тот факт, что группы lambdas и группы методов имеют разные правила конвертирования, является довольно неудачным.
Итак, суммируя, компилятор на самом деле является правильной реализацией спецификации в этом случае, и этот конкретный сценарий является непреднамеренным последствием некоторых, возможно, неудачных вариантов выбора.