在 C# 中跨继承边界重载

本文关键字:边界 重载 继承 | 更新日期: 2023-09-27 18:35:29

读完这篇文章和那篇文章后,我感到困惑。

它说:

如果层次结构的不同级别有两个方法,则 "更深"的将首先被选择,即使它不是一个"更好的功能 成员"的呼叫。

也-

事实证明,如果您重写子级中的基类方法 类,这不算声明它。

现在让我们回到我的问题:

案例1

    public class Base
     {
           public virtual void  Foo(int x)  { "1".Dump();}
     }
    public class Child : Base
     {
          public void Foo(object x) { "3".Dump();}  
          public override void  Foo(int x)  { "2".Dump();}
     }

void Main()
{
    Child c = new Child();
    c.Foo(10); //emits 3
}

还行。根据文章

"

更深"的将首先被选择,即使它不是一个"更好的功能。而且它不计算覆盖...

所以这是正确的,程序发出"3"。(Foo(object x)被执行)

让我们更改 1 行的行顺序

案例2

          public class Base
         {
                 public virtual void  Foo(int x)  { "1".Dump();}
                 public void Foo(object x) { "3".Dump();} //<line being moved here
         }
        public class Child : Base
         {
              public override void  Foo(int x)  { "2".Dump();}
         }

    void Main()
    {
        Child c = new Child();
        c.Foo(10); //emits 2 !!!!
    }

现在它发出"2"。

现在让我们将所有 int 更改为对象,将所有对象更改为 int

案例3

      public class Base
    {
      public virtual void  Foo(object x)  { "1".Dump();}
      public void Foo(int x) { "3".Dump();} 
    }
    public class Child : Base
    {
         public override void  Foo(object x)  { "2".Dump();}
    }

void Main()
{
    Child c = new Child();
    c.Foo(1); //emits "3"
}

问题


问题#1:在案例2中,Child从其父亲那里继承了Foo(object x),并且他还覆盖了一个方法。

但我们刚才不是说的吗:

事实证明,如果您重写子级中的基类方法 类,这不算声明它

???

事实上,我们也没有声明继承的函数......那么在这种情况下的规则是什么


问题#2:在案例3中,Child从其父亲那里继承了Foo(int x),并且他还覆盖了一个方法。

但是现在,他选择了它的父亲功能。

似乎override只有在完全匹配的情况下才能获胜。

再说一次,在这种情况下的规则是什么


在 C# 中跨继承边界重载

查看类型

T 中名称 N 的成员查找过程(在您的情况下,成员Foo在类型 Child 中):

首先,构造在 T 中声明的所有可访问(第 3.5 节)名为 N 的成员和 T 的基类型(第 7.3.1 节)的集合:

virtual void Foo(int x) // Base
void Foo(object x) // Base
override void Foo(int x) // Child

包含重写修饰符的声明将从集合中排除。

virtual void Foo(int x) // Base
void Foo(object x) // Base

参数具有整数类型。因此,这里的最佳选择是(参数类型与参数类型匹配)

virtual void Foo(int x) // Base

而这个方法叫。但这是虚拟方法。并且由于虚拟方法调用机制而调用它:

对于在类中声明或由类继承的每个虚拟方法,都有 存在该方法的最派生实现 那个类。虚拟方法 M 的最派生实现 R类的确定如下:

  • 如果 R 包含引入 M 的虚拟声明,那么这是 M 派生最多的实现。
  • 否则,如果 R 包含 M 的覆盖,则这是 M 派生最多的实现。
  • 否则,M 相对于 R 的最派生实现与 M 关于 R 的最派生实现相同 R 的直接基类。

virtual void Foo(int x)方法相对于类Child的最派生实现是什么?是的,它是

override void Foo(int x) // Child

哪个被调用。在第三个样本中应用了相同的规则。但是,当重写方法删除后剩下两个选项时,最佳选择(由于参数类型)是非虚拟方法。

有两件事发生:

  1. 选择要调用的方法时,编译器将根据链接的文章遵守规则 - 即,即使在较浅的方法更具体匹配之前,也会选择更深的方法,然后再选择较浅的方法
  2. 如果调用重写的方法
  3. ,它将始终调用重写的方法(这与选择要调用的方法分开),调用基方法的"唯一"方法是从子类和使用基。我的方法(..)

所以基本上查找规则可以选择在更深的类上采用 int 的方法,但是当该方法被调用时,如果它被覆盖,它将调用在子类上采用 int 的方法。

我认为你只是通过阅读这两篇文章来搞砸你的事实。它的行为应该像任何其他正常的重写函数一样,因为子类中没有重载,因此没有什么可供选择的。让我们举一个简单的例子:

public class A
{
  public virtual void Foo()
  {
    Console.WriteLine("A.Foo() called");
  }
}
public class B: A
{
  public override void Foo()
  {
    Console.WriteLine("B.Foo() called");
  }
}
void Main()
{
new B().Foo();
}

预期输出是多少?显然,没有人会说B.Foo()打来了。好吧,在您的情况下,同样的事情正在发生。基类有更好的重载方法并不重要,子类只是赢了它。不要把事情复杂化。

关于情况 3,我不完全确定这种情况,但这里发生的事情是编译器尝试隐式转换为对象,然后它发现有一个具有相同签名的更深层次的方法,因此

规则

如果层次结构的不同级别有两个方法,则 "更深"的将首先被选择,即使它不是一个"更好的功能 成员"的呼叫

现在,一旦它转到基类,它就会注意到可以使用更好的方法,因此调用该方法。

PS:上面的解释是基于观察到的结果,可能不一定是上述行为的确切原因。