Мой вопрос мотивирован Эриком Липпертом в этом сообщении в блоге. Рассмотрим следующий код:
using System;
class Program {
class A {}
class B {}
static void M(A x, B y) { Console.WriteLine("M(A, B)"); }
static void Call(Action<A> f) { f(new A()); }
static void Call(Action<B> f) { f(new B()); }
static void Main() { Call(x => Call(y => M(x, y))); }
}
Это компилируется успешно и печатает M(A, B), потому что компилятор определяет, что типы x и y в лямбда-выражениях должны быть A и B соответственно. Теперь добавьте перегрузку для Program.M:
using System;
class Program {
class A {}
class B {}
static void M(A x, B y) { Console.WriteLine("M(A, B)"); }
static void M(B x, A y) { Console.WriteLine("M(B, A)"); } // added line
static void Call(Action<A> f) { f(new A()); }
static void Call(Action<B> f) { f(new B()); }
static void Main() { Call(x => Call(y => M(x, y))); }
}
Это дает ошибку времени компиляции:
ошибка CS0121: вызов неоднозначен между следующими методами или свойствами: 'Program.Call(Action < Program.A > )' и 'Program.Call(Action < Program.B > )'
Компилятор не может вывести типы x и y. Возможно, что x имеет тип A, а y имеет тип B или наоборот, и ни одна из них не может быть предпочтительной из-за полной симметрии. Все идет нормально. Теперь добавьте еще одну перегрузку для Program.M:
using System;
class Program {
class A {}
class B {}
static void M(A x, B y) { Console.WriteLine("M(A, B)"); }
static void M(B x, A y) { Console.WriteLine("M(B, A)"); }
static void M(B x, B y) { Console.WriteLine("M(B, B)"); } // added line
static void Call(Action<A> f) { f(new A()); }
static void Call(Action<B> f) { f(new B()); }
static void Main() { Call(x => Call(y => M(x, y))); }
}
Эта команда успешно компилируется и печатает M(A, B) снова! Я могу угадать причину. Компилятор разрешает перегрузку Program.Call, пытаясь скомпилировать лямбда-выражение x => Call(y => M(x, y)) для x типа A и для x типа B. Первый успешно, в то время как последний терпит неудачу из-за двусмысленности, обнаруженной при попытке вывести тип y. Поэтому компилятор заключает, что x должен иметь тип A.
Таким образом, добавление большей двусмысленности приводит к меньшей двусмысленности. Это странно. Более того, это не соответствует тому, что написал Эрик в вышеупомянутом сообщении:
Если у него более одного решения, компиляция не выполняется с ошибкой двусмысленности.
Есть ли веская причина для текущего поведения? Это просто вопрос облегчения жизни компилятора? Или это скорее недостаток компилятора/спецификации?