消除在重载调用中作为委托传递的重载方法之间的歧义
本文关键字:重载 歧义 方法 之间 调用 | 更新日期: 2023-09-27 18:06:49
假设我在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
),因此它进行编译。
我不打算写这样的代码;从编译器编写者的角度来看,这只是出于好奇。关于这个问题我找不到答案,因为很难用语言来表达。
我想知道在c#中是否有任何方法可以消除Main()
中给出的示例中的调用歧义,以便它可以编译。如何指定它,使其解析为传递SomeOverloadedMethod()
的CallWithDelegate(SomeDelegateWithoutParameters del)
或传递SomeOverloadedMethod(int n)
的CallWithDelegate(SomeDelegateWithParameter del)
?
有几种方法可以消除方法组重载解析的歧义。
方法1:强制转换方法组
CallWithDelegate((SomeDelegateWithoutParameters)SomeOverloadedMethod);
CallWithDelegate((SomeDelegateWithParameter)SomeOverloadedMethod);
这消除了重载的歧义。这种语法在实际环境中并不常见,但它是有效的(c# 5规范§6.6方法组转换):
与所有其他隐式和显式转换一样,强制转换操作符可用于显式执行方法组转换。
<子>[…]子>
方法组可能影响重载解析,并参与类型推断。
方法2:显式实例化委托
CallWithDelegate(new SomeDelegateWithoutParameters(SomeOverloadedMethod));
CallWithDelegate(new SomeDelegateWithParameter(SomeOverloadedMethod));
这与前面的方法相同,但没有语法糖。详细信息请参见§7.6.10.5委托创建表达式。
形式为
new D(E)
的委托创建表达式的绑定时间处理,其中D
是委托类型,E
是表达式,包括以下步骤:
- 如果
E
是方法组,则委托创建表达式的处理方式与E
到D
的方法组转换(§6.6)相同。<子>[…]子>
甚至还有一个与你的问题密切相关的例子:
如上所述,当从方法组创建委托时,委托的正式参数列表和返回类型决定选择哪个重载方法。在示例
中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
方法不存在,就会发生编译时错误。
方法3:使用lambda
CallWithDelegate(() => SomeOverloadedMethod());
CallWithDelegate(i => SomeOverloadedMethod(i));
CallWithDelegate((int i) => SomeOverloadedMethod(i)); // Explicit types, if needed
这种形式没有歧义,但它具有间接性(调用lambda,然后调用目标方法)。这可以通过JIT进行优化,而且很可能不会对性能产生明显的影响。
方法4:使用匿名委托CallWithDelegate(delegate() { SomeOverloadedMethod(); });
CallWithDelegate(delegate(int i) { SomeOverloadedMethod(i); });
这相当于lambda调用,但它使用更笨重(更老)的delegate
语法。
如果你想知道确切的重载解析规则,它们在规范§7.5.3重载解析中有描述。
虽然我不是编译器专家,但我可以告诉你,因为你提供了两个重载的方法来匹配这两个方法,编译器没有办法识别你的实际意图。它不能编译,因为在编译时目前没有实际的识别信息,正如Lucas所提到的,你可以强制转换来消除歧义。对于编译器来说,要解决这个问题,它需要运行时信息,根据您可能尝试传递的参数,您实际想要使用哪个方法。