【问题标题】:Simple C# OOP inheritance query简单的 C# OOP 继承查询
【发布时间】:2015-06-05 05:59:26
【问题描述】:

给定以下类:

interface IShape
{
    void Draw();
}

class Rectangle : IShape
{
    public void Draw() { }
}

class Square : Rectangle
{
    public new void Draw()  { }
}

有人能解释一下第二个 Draw 方法会发生什么吗?

Rectangle rect = new Rectangle();
rect.Draw();
rect = new Square();
rect.Draw();

顺便说一句,我说会调用Square类的Draw方法,但是当我进入VS2013时,调用了Rectangle类的Draw方法。

【问题讨论】:

  • 研究覆盖隐藏。那里有很多材料。
  • 这实际上将开始涉足一些关于如何方法本身被调用和绑定的深层概念。将虚拟表添加到您的研究中。
  • 这是一个对所有程序开放的问题。让优雅的代码之战开始。

标签: c# visual-studio oop inheritance


【解决方案1】:

Square 类中,new 关键字告诉编译器这是一个全新的方法,恰好与继承的方法具有相同的签名。因为是巧合,所以不应该重写继承的方法。

在这种情况下,当通过Square 变量调用Draw() 时,编译器将选择新方法。如果在这种情况下它没有选择新的方法,你想一想,你根本没有办法调用它。如果你想调用旧的Draw(),你仍然可以通过Rectangle 变量调用它(即使在同一个实例上)。

这称为hiding,您可以使用任何方法进行操作。你不会经常需要它——如果可能的话,你应该避免它,因为虽然它的语义定义良好,但它会让你的源代码有点混乱。


相反,如果你想表明这不是巧合,并且你故意用相同的签名覆盖继承的方法,你应该使用关键字override 关于你的新方法。在这种情况下,当在对象上调用 Draw() 时,无论您从哪个变量调用它,您将始终获得用于创建该对象的类的 Draw() 版本(new Square()new Rectangle() 等) .

请记住,被覆盖的方法必须首先允许自己被覆盖。为此,必须首先将其标记为 virtual - 不能覆盖未标记为 virtual 的方法。

正如关键字所说,这称为overriding。你会经常看到和使用这个,因为它启用了polymorphism,这是面向对象编程的核心概念之一。

【讨论】:

    【解决方案2】:

    如果Draw 方法是虚拟的,那么对象的实际类型将用于确定调用哪个方法,即Square.Draw 方法。

    由于该方法不是虚拟的,因此决定调用哪个方法的是引用的类型,即Rectangle.Draw 方法。

    【讨论】:

      【解决方案3】:

      它将调用DrawRectangle版本。

      这是因为变量rect 在编译时是Rectangle 类型。因为您已经通过new 覆盖了SquareDraw 方法,所以运行时无法在运行时查找Square 版本的Draw - 这将是虚拟表或V 表中的查找,它在这种情况下不存在,因为 Draw 不是 virtual

      注意以下将调用Square版本的Draw

      Square sq = new Square();
      sq.Draw();
      

      因为sq 在编译时已知为Square

      如果您要在Rectangle 中将Draw 设为virtual,并将new 更改为override,则原始代码将调用Square.Draw。这是允许的,即使Draw 是接口方法的实现。

      也可以创建一个新的接口方法。考虑以下几点:

      internal interface IShape
      {
          void Draw();
      }
      
      internal class Rectangle : IShape
      {
          public void Draw() { }
      }
      
      internal class Square : Rectangle, IShape
      {
          public new void Draw() { }
      }
      

      这种情况下的代码

      IShape rect = new Rectangle();
      rect.Draw();
      rect = new Square();
      rect.Draw();
      

      将调用第二种形式。

      【讨论】:

        【解决方案4】:

        当您使用new 而不是override 时,该方法不会被覆盖,而是被隐藏。

        class Animal
        {
            public virtual void MakeSound() { Console.WriteLine("Animal called"); } 
        }
        
        class Dog : Animal
        {
            // Note: You can not use override and new in the same class, on 
            // the same method, with the same parameters.  This is just to 
            // show when "new" takes effect, and when "override" takes effect.
            public override void MakeSound() 
            { 
                Console.WriteLine("Animal's MakeSound called for dog"); 
            } 
        
            public new void MakeSound()
            {
                Console.WriteLine("Dog's MakeSound called"); 
            }
        }
        
        public static class Program
        {
            public static void Main(string[] args)
            {
                Dog dogAsDog = new Dog();
                Animal dogAsAnimal = dogAsDog;
        
                // prints "Dog's MakeSound called"
                dogAsDog.MakeSound();
        
                // prints "Animal's MakeSound called for dog"
                dogAsAnimal.MakeSound();
            }
        }
        

        【讨论】:

          【解决方案5】:

          由于您已将其装箱为 Rectangle,并且您的 Square 类隐藏而不是覆盖 Rectangle 的 Draw(),因此第二次调用将执行 Rectangle 的 Draw 方法。

          【讨论】:

            猜你喜欢
            • 2011-12-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-25
            • 2018-05-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多