为什么C#编译器使用无效方法的重载?

我被下面的代码弄糊涂了

class A { public void Abc(int q) { Console.Write("A"); } } class B : A { public void Abc(double p) { Console.Write("B"); } } ... var b = new B(); b.Abc((int)1); 

代码执行的结果是写入控制台的“B”。

实际上B类包含两个重载的Abc方法,第一个是int参数,第二个是double。 为什么编译器使用整数参数的双重版本?

注意方法abc(double)不会影响或覆盖方法abc(int)

由于编译器可以隐式地将int转换为double,所以它selectB.Abc方法。 Jon Skeet在这篇文章中解释了这个问题 (search“implicit”):

方法调用的目标是Childtypes的expression式,因此编译器首先查看Child类。 这里只有一个方法,它是适用的(有一个从int到double的隐式转换),所以这是被挑选的那个。 编译器根本不考虑Parent方法。

原因是为了减less脆弱的基类问题的风险…

更多来自Eric Lippert

正如标准所述,“如果派生类中的任何方法都适用,基类中的方法不是候选者”。

换句话说,重载parsingalgorithm首先通过search类来find适用的方法。 如果find一个,那么深度基类中所有其他可用的方法将从候选集中移除以进行重载parsing。 由于Delta.Frob(float)是适用的,Charlie.Frob(int)甚至不被视为候选者。 只有在派生types中找不到适用的候选人时,我们才会开始查看其基类。

如果我们用这个从A下降的额外类来扩展你的问题中的例子,事情会变得更有趣一些:

 class C : A { public void Abc(byte b) { Console.Write("C"); } } 

如果我们执行下面的代码

 int i = 1; b.Abc((int)1); b.Abc(i); c.Abc((int)1); c.Abc(i); 

结果是BBCA 。 这是因为在B类的情况下,编译器知道它可以隐式地将任何 int转换为double。 在C类的情况下,编译器知道它可以将字符型int 1转换为一个字节(因为值1适合于一个字节),所以C的Abc方法被使用。 但是,编译器不能隐式地将任何旧的int转换为一个字节,所以c.Abc(i)不能使用C的Abc方法。 在这种情况下,它必须使用父类。

隐式数字转换的这个页面显示了一个紧凑的表格,其中的数字types具有向其他数字types的隐式转换。

即使将B定义为:也可以获得相同的function:

 class B : A { public void Abc(object p) { Console.Write("B"); } } 

简而言之,这是因为重载parsing是通过查看当前类中定义的方法来完成的。 如果在当前课程中有任何合适的方法,它将停止查找。 只有在没有合适的匹配的情况下,才会查看基类

您可以查看“ 超载”分辨率规格以获得详细的解释。

不同的语言(如C ++,Java或C#)具有完全不同的重载parsing规则。 在C#中,根据语言规范正确select了重载。 如果你想要select其他的超载,你可以select。 记住这一点:

当一个派生类想要为一个inheritance的方法声明另一个重载时,为了把所有可用的重载视为等权的对等体,它也必须明确地覆盖所有inheritance的重载。

要求这个练习的语言devise好处是什么?

想象一下,你正在使用第三方库(比如说.NET框架)并从它的一个类派生出来。 在某个时候,你引入了一个叫做Abc的私有方法(一个新的,唯一的名字,而不是任何东西的重载)。 两年后,升级第三方库版本时,不会注意到它们还添加了一个可供您访问的方法,令人遗憾的是, Abc除了在某个地方有不同的参数types之外(因此升级不会提示您编译时间错误),它的行为有微妙的不同,甚至完全不同。 你真的想要一半你的私人电话到美国Abc公司被默默redirect到第三方美国Abc ? 在Java中,这可能会发生。 在C#或C ++中,这不会发生。

C#方式的优点是对于重新分发的库来说,在保持向后兼容性的同时增加function是比较容易的。 实际上有两种方式:

  • 您不会在自己的代码中混淆客户的私有方法调用。
  • 通过添加一个新的唯一命名方法,您永远不会破坏您的客户,尽pipe在添加您自己现有方法的重载之前,您仍然需要三思而行。

C#方式的不足之处在于它削弱了OOP理念,即重写只改变实现的方法,而不改变类的API。