【发布时间】:2016-10-25 21:07:00
【问题描述】:
我的问题是由 Eric Lippert 的 this blog post 引起的。考虑以下代码:
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),因为编译器计算出lambda表达式中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.Call(Action )”
编译器无法推断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 类型为A 和x 类型为B 编译lambda 表达式x => Call(y => M(x, y))。前者成功,而后者因为在尝试推断y 的类型时检测到的歧义而失败。因此,编译器得出结论x 必须是A 类型。
因此添加更多的歧义会导致更少的歧义。这很奇怪。而且,这与 Eric 在上述帖子中所写的内容不一致:
如果它有多个解决方案,则编译失败并出现歧义错误。
当前的行为有什么好的理由吗?仅仅是让编译器的生活更轻松的问题吗?还是编译器/规范中的缺陷?
【问题讨论】:
-
原因是方法的返回类型不是其签名的一部分。因此,在解决正确的重载时,编译器只查看方法的参数。
-
@viveknuna 所有方法都返回
void,返回类型与什么有什么关系?
标签: c# lambda overload-resolution