简单的C#面向对象继承问题

3

以下是给定的类:

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方法被调用了。


5
研究“覆盖”和“隐藏”的区别。有很多相关资料。 - D Stanley
这实际上将开始涉及一些关于如何调用和绑定方法的深层概念。在你的研究中加入虚拟表。 - David
这是一个面向所有人的程序问题。让优雅的代码之战开始。 - Aizen
5个回答

4
Square类中,new关键字告诉编译器这是一个全新的方法,只是恰好具有与继承方法相同的签名。因为这只是巧合,所以不应该覆盖继承方法。在这种情况下,当通过Square变量调用Draw()时,编译器将选择新方法。如果在这种情况下没有选择新方法,您将无法调用它。如果您想调用旧的Draw(),仍然可以通过Rectangle变量调用它(即使是在同一实例上)。 这称为隐藏,您可以对任何方法使用它。您不经常需要它,如果可能的话,应该避免使用它,因为虽然其语义是明确定义的,但它会使您的源代码有点混乱。
相反,如果您希望指示这不是巧合,而是有意用相同的签名覆盖继承的方法,则应在新方法上使用override关键字。在这种情况下,在对象上调用Draw()时,您将始终得到创建该对象的类的Draw()版本(new Square()new Rectangle()等),无论您从哪个变量调用它。
请记住,被覆盖的方法必须首先允许自己被覆盖。为此,它必须首先标记为virtual——未标记为virtual的方法不能被覆盖。 这称为覆盖,正如关键字所说的那样。您会经常看到和使用这一个,因为它启用了多态,这是面向对象编程的核心概念之一。

2
如果Draw方法是虚拟的,那么实际对象的类型将被用于确定调用哪个方法,即Square.Draw方法。
由于该方法不是虚拟的,因此引用类型决定了调用哪个方法,即Rectangle.Draw方法。

1
它将调用DrawRectangle版本。
原因是在编译时,变量rect的类型为Rectangle。通过使用new重写了SquareDraw方法后,运行时无法在Virtual Table或V-Table中查找Square版本的Draw - 在这种情况下,由于Draw不是virtual,因此不存在V-Table。
请注意,以下代码将调用Square版本的Draw
Square sq = new Square();
sq.Draw();

因为在编译时,sq 被认为是一个 Square

如果你将 DrawRectangle 中定义为 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();

将调用第二个表单。


1
当您使用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();
    }
}

0

由于您将其封装为矩形,而您的正方形类隐藏而不是覆盖矩形的Draw()方法,因此第二个调用将执行矩形的Draw方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接