代码之家  ›  专栏  ›  技术社区  ›  Daniel May

为什么这个多态代码会打印它的功能?

  •  66
  • Daniel May  · 技术社区  · 17 年前

    多态性 和 在OOP-C#中。

    // No compiling!
    public class A
    {
         public virtual string GetName()
         {
              return "A";
         }
     }
    
     public class B:A
     {
         public override string GetName()
         {
             return "B";
         }
     }
    
     public class C:B
     {
         public new string GetName()
         {
             return "C";
         }
     }
    
     void Main()
     {
         A instance = new C();
         Console.WriteLine(instance.GetName());
     }
     // No compiling!
    

    现在,在与提出这个谜题的其他开发人员进行了长时间的交谈之后,我知道输出是什么,但我不会为您破坏它。我真正遇到的唯一问题是我们如何获得输出,代码如何逐步完成,继承了什么,等等。

    我想 C B 将返回,因为C继承 B B 也继承 A


    问题:

    5 回复  |  直到 5 年前
        1
  •  100
  •   Eric Lippert    16 年前

    1. 每个插槽的内容是什么?
    2. 叫哪个插槽?

    让我们先考虑一下插槽。有两个插槽。A的所有实例都需要有一个插槽,我们称之为getnamelota。C的所有实例都需要有一个我们称之为GetNameSlotC的插槽。这就是C语言声明中“new”的意思——它的意思是“我想要一个新槽”。与B中声明的“override”相比,B中声明的“override”意味着“我不想要新插槽,我想要重用getnamelota”。

    当然,C继承自A,因此C还必须有一个插槽GetNameSlotA。因此,C的实例有两个插槽——GetNameSlotA和GetNameSlotC。非C的A或B实例有一个插槽GetNameSlotA。

    现在,当您创建一个新的C时,这两个槽中会有什么?有三种方法,我们称之为GetNameA、GetNameB和GetNameC。

    B的声明说“将GetNameB放在getnamelota中”。B是C的超类,所以B的规则适用于C的实例。现在a和B之间存在冲突。B是更派生的类型,所以它赢了——B的规则 覆盖

    C的声明说“将GetNameC放在getnamelotc中”。

    因此,您的新C将有两个插槽。GetNameSlotA将包含GetNameB,GetNameSlotC将包含GetNameC。

    现在,我们已经确定了哪些方法位于哪些插槽中,所以我们回答了第一个问题。

    现在我们必须回答第二个问题。这个插槽叫什么?

    因此,这是对GetNameSlotA中的任何内容的调用。我们已经确定,在运行时,GetNameB将位于该插槽中。因此,这是对GetNameB的调用。

    这里的关键是

        2
  •  24
  •   Blindy    17 年前

    它应该返回“B”,因为 B.GetName() 放在虚拟的小桌子盒子里 A.GetName() 作用 C.GetName() 是一个编译时“重写”,它不会重写虚拟表,因此无法通过指向的指针检索它 A .

        3
  •  3
  •   Thomas Weller    17 年前

    很简单,您只需要记住继承树。

    推翻 (请注意,只有“virtual”是覆盖,“new”是完全不同的…)。

    类“C”中的“New”的意思正是:如果您对这种(确切的)类型的实例调用“GetName()”,那么请忘记一切并使用此方法相反,Virtual的意思是:无论调用实例的确切类型是什么,都要在继承树上找到具有此名称的方法。

        4
  •  2
  •   Mange    13 年前

    好吧,这篇文章有点老了,但它是一个很好的问题和回答,所以我只想补充一下我的想法。

    考虑下面的例子,它与以前相同,除了主要功能:

    // No compiling!
    public class A
    {
        public virtual string GetName()
        {
            return "A";
        }
    }
    
    public class B:A
    {
        public override string GetName()
        {
            return "B";
        }
    }
    
    public class C:B
    {
        public new string GetName()
        {
            return "C";
        }
    }
    
    void Main()
    {
        Console.Write ( "Type a or c: " );
        string input = Console.ReadLine();
    
        A instance = null;
        if      ( input == "a" )   instance = new A();
        else if ( input == "c" )   instance = new C();
    
       Console.WriteLine( instance.GetName() );
    }
    // No compiling!
    

    现在很明显,函数调用不能在编译时绑定到特定函数。然而,必须编译一些东西,这些信息只能取决于引用的类型。因此,除了C类型的引用之外,不可能使用任何引用来执行C类的GetName函数。

    顺便说一句,也许我应该用method这个词来代替function,但正如莎士比亚所说:任何其他名称的函数仍然是一个函数:)

        5
  •  -1
  •   Vladislav Rastrusny    17 年前

    实际上,我认为它应该显示C,因为新操作符只是隐藏 全部的 具有相同名称的祖先方法。因此,在隐藏A和B方法的情况下,只有C仍然可见。

    http://msdn.microsoft.com/en-us/library/51y09td4%28VS.71%29.aspx#vclrfnew_newmodifier