Мой вопрос мотивирован Эриком Липпертом в этом сообщении в блоге. Рассмотрим следующий код:
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
.
Таким образом, добавление большей двусмысленности приводит к меньшей двусмысленности. Это странно. Более того, это не соответствует тому, что написал Эрик в вышеупомянутом сообщении:
Если у него более одного решения, компиляция не выполняется с ошибкой двусмысленности.
Есть ли веская причина для текущего поведения? Это просто вопрос облегчения жизни компилятора? Или это скорее недостаток компилятора/спецификации?