假设我在C#中有这个:
class OverloadTest
{
void Main()
{
CallWithDelegate(SomeOverloadedMethod);
}
delegate void SomeDelegateWithoutParameters();
delegate void SomeDelegateWithParameter(int n);
void CallWithDelegate(SomeDelegateWithoutParameters del) { }
void CallWithDelegate(SomeDelegateWithParameter del) { }
void SomeOverloadedMethod() { }
void SomeOverloadedMethod(int n) { }
}
当然,这不会编译,因为行CallWithDelegate(SomeOverloadedMethod);
不明确。
现在,假设只有一个CallWithDelegate(SomeDelegateWithoutParameter del)
函数(没有重载)。在这种情况下,不存在歧义,因为从似乎正在发生的事情中,编译器可以查看参数类型并从候选列表中丢弃SomeOverloadedMethod(int n)
(因为它只能采用SomeDelegateWithoutParameters
),所以它编译。</ p>
我不打算写这样的代码;从编译器编写者的角度来看,这只是出于好奇。我无法找到这方面的答案,因为用语言表达起来很困惑。
我想知道C#中是否有任何方法可以消除给定示例中Main()
中的调用的歧义,以便编译。如何指定它以便它解析为CallWithDelegate(SomeDelegateWithoutParameters del)
传递SomeOverloadedMethod()
,或CallWithDelegate(SomeDelegateWithParameter del)
传递SomeOverloadedMethod(int n)
?
答案 0 :(得分:9)
有几种方法可以消除方法组的重载决策。
CallWithDelegate((SomeDelegateWithoutParameters)SomeOverloadedMethod);
CallWithDelegate((SomeDelegateWithParameter)SomeOverloadedMethod);
这消除了过载的歧义。这是非常罕见的语法,但它有效(C#5规范§6.6方法组转换):
与所有其他隐式和显式转换一样,转换运算符可用于显式执行方法组转换。
<子> [...] 子>
方法组可能会影响重载决策,并参与类型推断。
CallWithDelegate(new SomeDelegateWithoutParameters(SomeOverloadedMethod));
CallWithDelegate(new SomeDelegateWithParameter(SomeOverloadedMethod));
这与没有语法糖的前一种方法相同。有关更多详细信息,请参阅§7.6.10.5委派创建表达式中的规范。
new D(E)
形式的委托创建表达式的绑定时处理,其中D
是委托类型,E
是表达式,包含以下步骤:
- 如果
E
是方法组,则代理创建表达式的处理方式与方法组转换(第6.6节)从E
到D
的处理方式相同。<子> [...] 子>
甚至有一个与你的问题密切相关的例子:
如上所述,当从方法组创建委托时,委托的形式参数列表和返回类型确定要选择哪个重载方法。在示例中
delegate double DoubleFunc(double x); class A { DoubleFunc f = new DoubleFunc(Square); static float Square(float x) { return x * x; } static double Square(double x) { return x * x; } }
使用引用第二个
A.f
方法的委托初始化Square
字段,因为该方法与形式参数列表和返回类型DoubleFunc
完全匹配。如果第二个Square
方法不存在,则会发生编译时错误。
CallWithDelegate(() => SomeOverloadedMethod());
CallWithDelegate(i => SomeOverloadedMethod(i));
CallWithDelegate((int i) => SomeOverloadedMethod(i)); // Explicit types, if needed
这个表单不含糊,但它有一个间接(lambda被调用,然后它调用目标方法)。这可能会被JIT优化,但它最有可能不会产生明显的性能影响。
CallWithDelegate(delegate() { SomeOverloadedMethod(); });
CallWithDelegate(delegate(int i) { SomeOverloadedMethod(i); });
这相当于lambda调用,但它使用了较大的(和较旧的)delegate
语法。
如果您想知道确切的重载决策规则,则会在§7.5.3重载决策中的规范中对它们进行描述。
答案 1 :(得分:-1)
虽然我不是编译专家,但我可以告诉您,由于您提供了两种与这两种方法匹配的重载方法,因此编译器无法识别您的实际意图。它无法编译,因为在编译时,目前没有像Lucas现在提到的实际识别信息,您可以强制转换以消除歧义。对于编译器来解决这个问题,它需要运行时信息,根据您可能尝试并传入的参数,您实际想要使用哪种方法。