C#函数的继承和覆盖

时间:2013-07-08 07:06:07

标签: c# inheritance

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. 子项内的函数foobar()隐藏了父()
  4. bar()

    所有这三个函数都使用不同的技术来隐藏父函数。任何人都可以指导我,这三者之间有什么不同,以及使用哪一种?

3 个答案:

答案 0 :(得分:2)

首先,1和3是相同的,编译器/ IDE(Visual Studio)会自动假设你打算做第3号。你会注意到它在名称{{1}下面放了一个绿线并告诉您使用foo()关键字。所以实际上,只有两种类型的基于继承的方法改变:

  1. 方法覆盖
  2. 方法隐藏
  3. 方法覆盖是指一个类声明其方法是虚拟的,这实际上表示“任何继承自我的类都可以自由地覆盖它”。这意味着无论如何,如果子类重写该方法,则调用被覆盖的版本。

    另一方面,方法隐藏是孩子叛逆并且说“只要我作为一个儿童班存在,我就会忽略我的父实现而不是这样做”。这里的诀窍是,当子类被视为其父类时,父类的实现将被调用。

    以下是一个示例,假设您正在使用类设置:

    new

    请注意,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.");
        }
    }
    

    现在您看到,即使两个继承的类都被视为其父类,但重写的方法对于继承的类是唯一的。这允许父类定义它如何工作的基本轮廓,而子类改进这一点并更具体地做事。然而,因为子类可以完成父级所能做的所有事情,所以可以将其视为父级。

    这个概念被称为多态。

答案 1 :(得分: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关键字。

它说,你在子类方法中声明了与父类中的方法相同的签名,隐藏父类实现。从最后一个样本中可以看出,隐藏与覆盖不同。当您拥有父类型的变量时,将调用在子级中重写的成员。但是在最后一种情况下不会调用隐藏方法。

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

答案 2 :(得分: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();