【问题标题】:C# overload with override带有覆盖的 C# 重载
【发布时间】:2012-06-01 13:59:14
【问题描述】:

我当然可以自己编写一个虚拟测试来回答这个问题,但我想知道人们对这个问题的看法。这里是:

当我们同时重载和覆盖时会调用哪个方法?我只考虑类型重载而不是重载,并且当类型重载相关时。

让我举个例子:

class AA {}
class BB : AA {}

class A {
    public virtual void methodA(AA anAA) { Console.Write("A:methodA(AA) called"); }
    public virtual void methodA(BB aBB) { Console.Write("A:methodA(BB) called"); }
}

class B : A {
    public override void methodA(AA anAA) { Console.Write("B:methodA(AA) called"); }
}

new B().methodA(new BB());     // Case 1
new B().methodA(new AA());     // Case 2
new B().methodA((AA)new BB()); // Case 3

你能说出情况 1、2 和 3 会发生什么吗?

我个人认为超载是邪恶的,并且没有一致的想法可以得出可预测的答案。这完全基于编译器+vm中实现的约定。

编辑:如果您对为什么超载是邪恶的有疑问,您可以阅读来自Gilad Brach 的博客文章

谢谢

【问题讨论】:

    标签: c# overloading overriding


    【解决方案1】:

    不,这是完全可以预测的。首先解析方法签名——即首先确定重载。然后,调用最覆盖的方法。所以输出将是:

    • A:methodA(BB) 调用
    • B:methodA(AA) 调用
    • B:methodA(AA) 调用

    后两种情况下会调用获取AA实例的方法,因为这是传入的引用类型,调用的是B的版本。请注意,即使这样也会产生相同的结果:

    A instance = new B();
    instance.methodA((AA)new BB()); // Case 3
    

    【讨论】:

    • 我不是说代码不可预测。让我澄清一下。在这个例子中,编译器必须在语言中再添加 2 个公理。在覆盖之前应用第一个重载。它本来可以反过来,这是完全可以接受的。第二公理动态类型用于覆盖方法查找。我可以添加第三个公理。静态类型用于重载查找。我拥有的所有公理都是不可预测的。任何其他语言都可以选择一套完全不同的公理。
    • 编译器解析任何强类型语言的方法签名。这必须首先出现。覆盖由 Callvirt IL 操作代码处理,该操作代码运行当时所讨论对象中该方法的任何版本。因为编译器不知道该对象将是什么,所以必须稍后处理。我不同意。顺序必须是 C#、Java 等中的顺序...
    • 我宁愿使用术语“编译时间”或“运行时间”,因为它们比“稍后时间”更精确。但是假设“稍后时间”是运行时,您所描述的是您可以在任何 OO 语言中找到的经典方法查找(CLOS、Simula、Smalltalk 是发明这个概念的人)。但我要说的是,之前可能已经应用了覆盖,因此您最终在所有 3 种情况下都使用 callvirt IL。只有new A().methodA(new BB()) 会调用“A:methodA(BB)”方法。一种方式而不是另一种方式没有语义上的原因。
    【解决方案2】:

    当编译器确定要调用哪个方法时,从方法集中排除被覆盖的方法。请参阅member lookup 算法。因此,当您在 B 类型上调用 methodA 时,将构造来自 B 类型的名称为 methodA 的成员集,并且将构造它的基本类型:

    override B.methodA(AA)
    virtual A.methodA(AA)
    virtual A.methodA(BB)
    

    然后从集合中删除带有ovveride 修饰符的成员:

    virtual A.methodA(AA)
    virtual A.methodA(BB)
    

    这组方法是查找的结果。之后overload resolution 申请定义要调用的成员。

    1. A.methodA(BB) 被调用,因为它的参数匹配参数。
    2. A.methodA(AA) 会被选中,但它是虚方法,所以实际上调用到B.method(AA)
    3. 与选项 2 相同

    【讨论】:

    • 你的意思是 JIT 编译器,不是吗?
    • 不,我不是指 JIT 编译器。如果您没有合适的方法来调用,您将遇到编译时错误。
    【解决方案3】:

    我想结果会是这样的

    case 1 : Console.Write("A:methodA(BB) 被调用");

    案例 2:Console.Write("B:methodA(AA) 被调用");

    case 3 : Console.Write("B:methodA(AA) 被调用");

    在第 3 种情况下,它将看起来是它传递的类型,它是 B

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多