繁体   English   中英

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

[英]Why C# compiler use an invalid method's overload?

以下代码使我感到困惑

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,因此选择B.Abc方法。 乔恩·斯基特Jon Skeet)这篇文章对此进行了解释(搜索“隐式”):

方法调用的目标是类型Child的表达式,因此编译器首先查看Child类。 那里只有一种方法,并且它是适用的(从int到double的隐式转换),因此才被选择。 编译器根本不考虑Parent方法。

这样做的原因是为了减少发生脆性基类问题的风险。

Eric Lippert的更多内容

如标准所言,“如果派生类中的任何方法均适用,则基类中的方法不是候选对象”。

换句话说,重载解决方案算法从类中搜索适用的方法开始。 如果找到一个,则将更深的基类中的所有其他适用方法从候选集中删除,以进行重载解析。 由于Delta.Frob(float)适用,因此Charlie.Frob(int)甚至都不会被视为候选对象。 只有在最派生的类型中找不到适用的候选对象时,我们才开始查看其基类。

如果我们使用来自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方法。 在这种情况下,它必须使用父类。

此页面在隐式数值转换上显示了一个紧凑的表,其中数字类型具有到其他数值类型的隐式转换。

即使将B定义为以下内容,也可以获得相同的功能:

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

简而言之,这是因为重载解析是通过查看当前类中定义的方法来完成的。 如果当前类中有任何合适的方法,它将停止查找。 仅当没有合适的匹配项时,它才会查看基类

您可以查看过载分辨率规范以获得详细说明。

不同的语言(例如C ++,Java或C#)具有非常不同的重载解析规则。 在C#中,根据语言规范正确选择了重载。 如果要选择其他重载,则可以选择。 记住这一点:

当派生类打算为继承的方法声明另一个重载,以便将所有可用的重载都视为具有同等权利的对等体时,它还必须通过基调用显式重写所有继承的重载。

要求进行此练习的语言设计优势是什么?

想象一下,您正在使用第三方库(例如.NET框架),并且是从其一个类派生的。 在某个时候,您引入了一个称为Abc的私有方法(一个新的唯一名称,没有任何重载)。 两年后,您升级了第三方库版本,而没有注意到他们也添加了一种方法,您可以访问该方法,并且很遗憾地调用了Abc ,只是它在某处具有不同的参数类型(因此,升级不会通过编译来提醒您)时间误差),并且其行为略有不同,甚至目的也完全不同。 您是否真的希望将对Abc的私人通话的一半无声地重定向到第三方Abc 在Java中,可能会发生这种情况。 在C#或C ++中,这不会发生。

C#方式的好处是,对于重新分发的库,添加功能的同时要严格保持向后兼容性,这会比较容易。 实际上有两种方式:

  • 您将永远不会在客户自己的代码中弄乱客户的私有方法调用。
  • 您永远不会通过添加新的唯一命名方法来破坏客户,尽管在添加您自己现有方法的重载之前仍必须三思而行。

C#方式的缺点是,它会改变OOP的哲学,即仅更改实现而不更改类的API的重写方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM