16

什么时候使用新的虚拟关键词来装饰方法?感情是什么?就像定义一个接口,并添加一个类来继承接口。而是使用新的virtual来实现接口方法。

 interface IPrinter
{
    void Print();
}

 public class PrinterOne : IPrinter
{
    public void Print() 
    {
        Console.WriteLine("PrinterOne.");
    }
}

public class PrinterTwo : PrinterOne
{
    public new virtual void Print()
    {
        Console.WriteLine("PrinterTwo.");
    }
}

public class PrinterThree : PrinterTwo
{
    public override void Print()
    {
        Console.WriteLine("PrinterThree.");
    }
}

public class PrinterFour : PrinterThree
{
    public override void Print()
    {
        Console.WriteLine("PrinterFour.");
    }
}

 static void Main(string[] args)
    {
        IPrinter iprinter = new PrinterFour();
        iprinter.Print();//the output is PrinterOne? why???
        Console.ReadLine();
    }
4

4 回答 4

16

new并且virtual是两个(大部分)不相关的关键字。

new意味着它会影响基本方法。
virtual允许子类覆盖它。

通过接口调用方法会导致调用基方法,因为基方法不是virtual并且派生类没有显式地重新实现接口(这会导致方法被重新映射)

于 2012-05-02T14:36:11.430 回答
11

像这样使用的new关键字是成员隐藏。

请注意,我从未见过它与virtual关键字一起使用。它只是允许派生自方法实现PrinterTwooverride类型。Print

以这种方式使用的new关键字允许类型隐藏基类型的成员,但前提是您使用的是类型本身的变量。

例如,如果您要这样做:

PrinterOne one = new PrinterTwo();
one.Print();

不会调用该方法,PrinterTwo因为它不是继承链的一部分。

至于你什么时候会这样做......当你真的,真的需要出于一些我想不出的奇怪原因(也许是反思?)并且你不能在PrinterOne.

就个人而言,我永远不会这样做。

至于为什么输出是打印机一...调用IPrinter.Print将调用它定义的类型(PrinterOne在这种情况下),这将使您回到我上面的示例中,new除非您与具有它的类型交谈,否则该关键字被忽略.

基本上,使用IPrinter类似于PrinterOne我上面的小例子中的使用。

要解决问题,请制作PrinterOne方法virtual并完全删除new virtualin的使用PrinterTwo

于 2012-05-02T14:36:02.677 回答
5

新修饰符

http://msdn.microsoft.com/en-us/library/435f1dw2.aspx

当用作修饰符时,new 关键字显式隐藏从基类继承的成员。

这意味着该方法不会覆盖虚拟基类方法,但在派生类的实例上调用时仍具有优先权。换句话说,新方法只影响派生类的一个变量,而不影响基类。

虚拟修饰符

http://msdn.microsoft.com/en-us/library/9fkccyh4.aspx

virtual 关键字用于修改方法、属性、索引器或事件声明,并允许在派生类中重写它。

这意味着可以在派生类中重写该方法。当您在基类变量上调用虚拟方法时,该变量包含已覆盖虚拟方法的派生类的实例,将调用派生类实现。这与 new 关键字的行为相反。

于 2012-05-02T14:42:43.803 回答
2

这称为方法隐藏。当您需要为无法覆盖的方法提供自己的实现时,您可以使用它。因为PrinterOne.Print不是虚方法,所以不能被覆盖。相反,该new关键字用于创建隐藏原始方法的相同方法签名。将改为使用新方法。向此添加virtual关键字,允许您的新方法被派生类覆盖。

PrintTwo只有当您通过定义容器(例如。 )调用它时,才会调用隐藏原始的新方法。通过接口调用它调用原始方法。请注意,该方法从未被删除或替换,因此通过直接访问接口,原始实现仍然存在。

于 2012-05-02T14:38:29.270 回答