消除在重载调用中作为委托传递的重载方法之间的歧义

时间:2015-08-06 21:27:24

标签: c# overload-resolution

假设我在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)

2 个答案:

答案 0 :(得分:9)

有几种方法可以消除方法组的重载决策。

方法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是方法组,则代理创建表达式的处理方式与方法组转换(第6.6节)从ED的处理方式相同。
  •   
     

<子> [...]

甚至有一个与你的问题密切相关的例子:

  

如上所述,当从方法组创建委托时,委托的形式参数列表和返回类型确定要选择哪个重载方法。在示例中

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重载决策中的规范中对它们进行描述。

答案 1 :(得分:-1)

虽然我不是编译专家,但我可以告诉您,由于您提供了两种与这两种方法匹配的重载方法,因此编译器无法识别您的实际意图。它无法编译,因为在编译时,目前没有像Lucas现在提到的实际识别信息,您可以强制转换以消除歧义。对于编译器来解决这个问题,它需要运行时信息,根据您可能尝试并传入的参数,您实际想要使用哪种方法。