3
class Parent
{
    public void foo() 
    {
        Console.WriteLine("Hello from the foo inside Parent");
    }

    public virtual void bar() 
    {
        Console.WriteLine("Hello from the bar inside Parent.");
    }

    public void foobar() 
    {
        Console.WriteLine("Hello from the foobar inside Parent.");
    }
}
class Child : Parent
{
    public void foo()
    {
        base.foo();
        Console.WriteLine("Hello from the foo inside Child");
    }        

    public override void bar() 
    {
        base.bar();
        Console.WriteLine("Hello from the bar inside Child.");
    }

    public new void foobar() 
    {
        base.foobar();
        Console.WriteLine("Hello from the foobar inside Child.");            
    }
}

以上是我用来测试我在 C# 中继承知识的代码,但我在这里有一个困惑:

  1. 子内部的函数foo()隐藏了foo()父级。
  2. 子内部的函数bar()隐藏了bar()父级。
  3. child 内部的函数foobar()隐藏了bar()parent()

这三个函数都使用不同的技术来隐藏父函数。谁能指导我,这三个之间有什么区别以及何时使用哪一个?

4

3 回答 3

3

首先,1 和 3 相同,编译器/IDE (Visual Studio) 会自动假设您打算执行第 3 项,如果您执行第 1 项。您会注意到它在名称下方放置了一条绿线foo()并告诉您使用new关键字. 所以实际上,只有两种类型的基于继承的方法更改:

  1. 方法覆盖
  2. 方法隐藏

方法覆盖是当一个类声明它的方法是虚拟的,它本质上是说“任何从我那里继承的类都可以自由地覆盖它”。这意味着无论如何,如果子类覆盖该方法,则会调用覆盖的版本。

另一方面,方法隐藏是孩子叛逆并说'只要我作为子类存在,我就会忽略我的父实现并改为这样做'。这里的诀窍是,当子类被视为其父类时,父类的实现被调用。

这是一个示例,假设正在使用您的课程设置:

Child c = new Child();
c.foo();
c.bar();
Parent p = c;
p.foo();
p.bar();

请注意,Child c由于其继承性,可以将其存储在父变量中。任何从父类继承的子类都可以被视为其父类的实例。

现在尝试将此类添加到组合中:

class MaleChild : Parent
{
    public new void foo()
    {
        base.foo();
        Console.WriteLine("Hello from the foo inside MaleChild");
    }

    public override void bar()
    {
        base.bar();
        Console.WriteLine("Hello from the bar inside MaleChild.");
    }
}

并运行类似的测试:

Parent[] p = new Parent[2];

p[0] = new Child();
p[1] = new MaleChild();

p[0].foo();
p[0].bar();
p[1].foo();
p[1].bar();

现在您会看到,即使两个继承类都被视为它们的父类,覆盖的方法对于继承类来说是唯一的。这允许父类定义其工作原理的基本大纲,而子类对此进行改进并更具体地做事。然而,因为子类可以做父类可以做的一切,所以它可以被视为父类。

这个概念被称为多态性。

于 2013-07-08T07:29:52.993 回答
2

要查看差异,请尝试以下代码:

Console.WriteLine("Parent->Parent");
Parent p = new Parent();
p.foo();
p.bar();
p.foobar();

Parent在这种情况下,您有指向类实例的类型变量Parent。这里没有隐藏或覆盖任何内容。调用父类的方法。现在创建Child类型的变量,它指向的实例Child

Console.WriteLine("Child->Child");
Child c = new Child();
c.foo();
c.bar();
c.foobar();

在这种情况下,子类的所有方法都会被调用。也没什么有趣的。现在创建Parent类型的变量,它指向的实例Child

Console.WriteLine("Parent->Child");
Parent p = new Child();
p.foo(); // you have warning and parent method is invoked
p.bar(); // child overridden method invoked
p.foobar(); // no warning, parent method is invoked

在这种情况下,您可以在操作中看到多态性 - 调用在子类中覆盖的方法。如果方法未被覆盖,则调用父方法。但是您将收到一条警告消息:

警告 1 'Namespace.Child.foo()' 隐藏了继承的成员 'Namespace.Parent.foo()'。如果打算隐藏,请使用 new 关键字。

它说,您在子类方法中声明了与父类中的方法具有相同签名的方法,它隐藏了父类的实现。正如您从上一个示例中看到的那样,隐藏与覆盖不同。当您有父类型的变量时,将调用在子中覆盖的成员。但是在最后一种情况下不调用隐藏方法。

通常你不需要这样的行为(即隐藏一些成员),这就是编译器警告你的原因。但是如果出于某种原因您需要这种行为(例如,您没有父类代码并且不能使父方法 virtual),那么您应该使用new关键字来抑制此警告并表明您了解自己在做什么。这就是为什么您看不到foobar方法的警告。

于 2013-07-08T07:49:06.010 回答
1

首先第一个和第三个是一样的,唯一不同的是第三个不会给出编译时警告

Child.foo()' 隐藏继承的成员 `Parent.foo()'。如果打算隐藏,请使用新关键字

当您有一个基本类型的变量并且不知道该变量指向的实例的类型时,继承的真正价值就会出现。

请检查前三个调用的输出以及它们与最后三个调用的不同之处。

Parent p = new Child();
Child c = new Child();

p.foo();
p.bar();
p.foobar();
Console.WriteLine("------------------------");
c.foo();
c.bar();
c.foobar();
于 2013-07-08T07:24:45.557 回答