7

在阅读了这篇文章那篇文章之后 - 我很困惑。

它说 :

如果层次结构的不同级别有两种方法,则将首先选择“更深”的方法,即使它不是调用的“更好的函数成员”。

还 -

事实证明,如果您在子类中重写基类方法,则不算作声明。

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

情况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 更改为 object并将所有object 更改为 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只有在完全匹配的情况下才能获胜。

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


4

3 回答 3

3

请参阅类型 T 中名称 N 的成员查找过程(在您的情况下Foo为 type中的成员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 的直接基类的最派生实现相同。

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

override void Foo(int x) // Child

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

于 2012-05-28T14:58:34.603 回答
3

有两件事发生:

  1. 选择要调用的方法时,编译器将按照链接的文章遵守规则 - 即。即使较浅的方法是更具体的匹配,也会在较浅的方法之前选择较深的方法
  2. 如果您调用被覆盖的方法,它将始终调用被覆盖的方法(这与选择调用哪个方法是分开的),调用基方法的“唯一”方法是从子类并使用 base.MyMethod(..)

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

于 2012-05-28T14:58:53.833 回答
1

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

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:以上解释是基于观察到的结果,不一定是上述行为的确切原因。

于 2012-05-28T17:36:03.040 回答