【问题标题】:C# complex inheritanceC# 复杂继承
【发布时间】:2013-05-14 07:38:57
【问题描述】:

假设我们有这样的代码:

interface I
{
    int P { get; }
}

class A : I
{
    public virtual int P { get { return 0; } }
}

class B : A
{
    public override int P { get { return 1; } }
}

class C : B, I
{
    public int P { get { return 2; } }
}

A c = new C();
I ic = new C();

现在的问题是 c.P 和 ic.P 应该是什么?其实我知道会是 1 和 2,但你能解释一下为什么吗?

【问题讨论】:

标签: c# inheritance


【解决方案1】:

A c = new C(); 的情况下,它将调用它找到的第一个正确覆盖A 中声明的函数的函数。由于C.P 没有覆盖它,而是隐藏了它,所以虚拟树遍历(多态解析)不会调用C.P 函数,而是调用继承树中最低的一个,即B.P .

对于I ic = new C();,它会很乐意调用P的直接接口实现,因为它不关心多态虚拟调用,所以在这种情况下它调用C.P

注意:这里的关键是C 在其继承声明中直接有I(即看起来像class C : B, I 而不是class C : B)对于这种行为。如果没有,ic.P 调用将再次引用被覆盖的继承 P,就像 c.P 一样,并且也将返回 1。

您应该会看到一条警告,其中包含以下内容,这有助于为您提供一些线索,表明某些事情做得不太正确:

'C.P'隐藏继承的成员'B.P'。要使当前成员覆盖该实现,请添加 override 关键字。否则添加 new 关键字。

【讨论】:

  • 我认为这个答案很好,但仍然遗漏了一些东西。问题还在于哪个方法被“映射”为接口的实现。这里非常重要的是,C 类在声明class C : B, I再次 提到了接口I。因为没有I 的这种“重复”,C 类型仍将实现I,因为继承自A。但是如果我们只有class C : B 而没有提到I,结果会有所不同。我建议提问者阅读C#语言规范中的Interface re-implementation部分。
  • @JeppeStigNielsen 好点,我应该更明确地提到这一点。我引用了 C 对 I 的直接实现,但是在重新阅读之后,我可以看到这可能是读者遗漏的一点。我会更新的。
【解决方案2】:

class B : A 表示 B 类继承了 A 类,简单来说就是 B 类将具有 A 类所具有的所有属性功能。但是当您说 public override int P 时,(“关键字”是覆盖)意味着对于 'P' 类 B 将以不同的方式表现。这就像为'P'“隐藏”类A。

更有用的是阅读文档:

Inheritance

【讨论】:

    【解决方案3】:

    This Page 与您的帖子非常相关。在这种情况下,会发生什么取决于当前类型。因此,对于cP 为 1,因为它使用来自 AP 方法,该方法已被 B 覆盖。它正在调用子链最下方的方法,该方法仍然是A 的方法。对于ic,该方法在子链的最下方再次调用,这次是在C,因为它是I 中存在的方法的有效实现。这在第一种情况下不会发生,因为A.P 没有被C.P 覆盖。

    【讨论】:

      【解决方案4】:

      让我们逐一分析:

      A c = new C();
      

      A 类将P 实现为virtual 方法。子类需要声明 P 添加 override 修饰符才能真正覆盖它(MSDN reference):

      需要 override 修饰符来扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现。

      现在是第二个:

      I ic = new C();
      

      接口I没有(显然)实现方法P,也没有将它声明为virtual(虽然它被隐式定义为virtual sealed),这意味着实例的实现是后期绑定的将使用到ic(早期绑定到接口I)。

      【讨论】:

        【解决方案5】:

        应该是 2 和 2。

        这是 1 和 2,因为您没有在 C 中正确实现 P。它在 B 中隐藏了 P。C 应该是:

        class C : B, I
        {
            public override int P { get { return 2; } }
        }
        

        这里需要关键字override,如果你绝对需要这种方式(将1和2作为对象状态),你需要使用new关键字:

        class C : B, I
        {
            public new int P { get { return 2; } }
        }
        

        如果您使用的是 Visual Studio,您应该已经看到了警告。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-05
          • 1970-01-01
          • 2011-01-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多