C#-关键字用法virtual + override vs.new

时间:2020-03-06 15:00:02  来源:igfitidea点击:

在基本类型" virtual"中声明一个方法,然后使用"override"关键字在一个子类型中重写该方法,与在以下情况中声明匹配方法时简单地使用"new`"关键字相比,有什么区别?孩子的类型?

解决方案

" new"关键字不会被覆盖,它表示一个与基类方法无关的新方法。

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

这将显示为false,如果我们使用了覆盖,则其将显示为true。

(摘自Joseph Daigle的基本代码)

因此,如果我们正在执行真正的多态性,则应始终覆盖。我们唯一需要使用" new"的地方是该方法与基类版本没有任何关系。

实际上,关键字" new"会创建一个仅在该特定类型上存在的全新成员。

例如

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

该方法在两种类型上都存在。当我们使用反射并获取类型为Bar的成员时,我们实际上会发现2种名为DoSomething()的方法,它们看起来完全一样。通过使用new,我们可以有效地将实现隐藏在基类中,这样,当类从Bar派生时(在我的示例中),对base.DoSomething()的方法调用将变为Bar而不是Foo。 。

Override关键字和new关键字之间的区别在于,前者执行方法重写,而后者执行方法隐藏。

查看以下链接以获取更多信息...

MSDN和其他

以下是一些代码,用于了解虚拟方法和非虚拟方法的行为差异:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

除了技术细节之外,我认为使用虚拟/覆盖可以传达设计上的许多语义信息。当我们将方法声明为虚拟方法时,表示我们希望实现类可能希望提供自己的非默认实现。同样,在基类中省略此方法也声明了以下期望:默认方法应足以满足所有实现的类。同样,可以使用抽象声明来强制实现类提供自己的实现。同样,我认为这传达了很多关于程序员期望代码使用方式的信息。如果我同时编写了基类和实现类,并且发现自己使用的是新的类,那么我将认真考虑是否不要在父级中使该方法虚拟化并明确声明我的意图的决定。

virtual / override告诉编译器这两个方法是相关的,并且在某些情况下,当我们认为我们正在调用第一个(虚拟)方法时,实际上调用第二个(重写)方法是正确的。这是多态性的基础。

(new SubClass() as BaseClass).VirtualFoo()

将调用子类的重写VirtualFoo()方法。

new告诉编译器我们要向派生类中添加一个方法,该方法的名称与基类中的方法相同,但彼此之间没有任何关系。

(new SubClass() as BaseClass).NewBar()

将调用BaseClass的NewBar()方法,而:

(new SubClass()).NewBar()

将调用子类的NewBar()方法。

我总是发现通过图片更容易理解这样的事情:

再次,以约瑟夫·戴格尔的代码,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

如果我们随后这样调用代码:

Foo a = new Bar();
a.DoSomething();

注意:重要的是我们的对象实际上是一个" Bar",但是我们将其存储在" Foo"类型的变量中(这与强制转换类似)。

然后结果将如下所示,具体取决于在声明类时使用的是"虚拟" /"覆盖"还是"新"。