【问题标题】:Overloading across inheritance boundaries in c#?在 C# 中跨越继承边界重载?
【发布时间】:2012-05-28 14:30:27
【问题描述】:

在阅读了这个 article 和那个 article 之后 - 我很困惑。

上面写着:

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

还有——

事实证明,如果你覆盖一个子类的基类方法 类,这不算声明它。

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

案例一

    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 只有在完全匹配的情况下才能获胜。

再次,规则是什么在这种情况下?


【问题讨论】:

  • 我希望这只是出于好奇。 IMO,如果您的代码行为依赖于语言规范的令人困惑的极端情况,那么它的设计很糟糕。
  • @TimS。当然....好奇心让我更好地理解....
  • 重载解析选择方法槽,然后重载被认为到达最派生的适用子类。情况 2 中选择了虚拟基方法(Child 的覆盖不算作声明);然后,由于它被覆盖,因此实际上调用了 Child 中的版本。

标签: c# .net .net-4.0 clr


【解决方案1】:

请参阅类型 T 中名称 N 的 member lookup process(在您的情况下,类型为 Child 中的成员 Foo):

首先,构造在 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

并且调用了这个方法。但它是虚拟方法。并且由于virtual method invocation机制而被调用:

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

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

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

override void Foo(int x) // Child

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

【讨论】:

  • 那么您的回答如何解释案例 1?
  • 在第一种情况下 Child 的方法没有 override 修饰符。并且根据参数列表Foo(object x)适用。因此,所有基类方法都从集合中删除。你可以找到关于方法调用描述的解释msdn.microsoft.com/en-us/library/aa691356(v=vs.71).aspx
  • 子方法确实有覆盖修饰符。
  • Child 有两种方法。带有override 修饰符的一个从集合中移除。我们有第二个孩子的方法public void Foo(object x) 没有override 修饰符。此方法适用,因此所有基类方法都从集合中删除。
【解决方案2】:

发生了两件事:

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

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

【讨论】:

  • 调用基方法的“唯一”方式是从子类并使用 base.MyMethod ?????它从它的父亲那里继承了 te func。这里不需要 base.xxx
  • 如果 xxx 被 Child 覆盖,那么除非你做一些花哨的步法,否则 child.xxx 总是会调用 Child 的 xxx 方法,如果你想调用 Parent 中定义的 xxx 方法Child 的实例,那么您需要从 Child 调用 base.xxx
【解决方案3】:

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

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,我对此并不完全确定,但这里发生的情况是编译器尝试隐式转换为对象,然后它发现有一个更深层次的方法具有相同的签名和规则

如果层次结构的不同级别有两种方法,则 将首先选择“更深”的,即使它不是“更好的功能” 成员”的通话

现在一旦它进入基类,它就会注意到有一个更好的方法可以使用,因此调用该方法..

P.S:以上解释基于观察到的结果,不一定是上述行为的确切原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-16
    • 2011-02-11
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    • 2012-06-02
    相关资源
    最近更新 更多