C# - 关键字用法虚拟+覆盖与新

时间:2008-10-01 22:06:52

标签: c# syntax override method-hiding member-hiding

在基本类型“virtual”中声明方法然后使用“override”关键字在子类型中覆盖它而不是简单地使用“{{1}”之间有什么区别}“在子类型中声明匹配方法时的关键字?

11 个答案:

答案 0 :(得分:221)

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

再次,采取joseph daigle的代码,

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 类型的变量中(这是类似的)施放它)

然后结果将如下,具体取决于您在声明课程时是否使用了virtual / overridenew

Virtual/Override explanation image

答案 1 :(得分:178)

“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”的唯一地方是该方法与基类版本无关。

答案 2 :(得分:41)

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

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;
    }
}

答案 3 :(得分:19)

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

例如

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

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

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

答案 4 :(得分:9)

虚拟/覆盖告诉编译器这两个方法是相关的,在某些情况下,如果你认为你正在调用第一个(虚拟)方法,那么调用第二个(被覆盖的)实际上是正确的方法而不是。这是多态性的基础。

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

将调用SubClass的覆盖VirtualFoo()方法。

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

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

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

(new SubClass()).NewBar()

将调用SubClass的NewBar()方法。

答案 5 :(得分:8)

除了技术细节之外,我认为使用虚拟/覆盖可以在设计上传达大量的语义信息。声明方法虚拟时,表明您希望实现类可能希望提供自己的非默认实现。同样地,在基类中省略这一点,声明期望默认方法应该足以满足所有实现类。类似地,可以使用抽象声明来强制实现类来提供自己的实现。同样,我认为这传达了很多关于程序员如何期望使用代码的信息。如果我正在编写基础和实现类,并发现自己使用新的,我会认真地重新考虑不要在父项中使该方法虚拟并明确声明我的意图。

答案 6 :(得分:4)

override关键字和new关键字之间的区别在于前者执行方法覆盖,后者执行方法隐藏。

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

MSDNOther

答案 7 :(得分:3)

  • new关键字用于隐藏。 - 表示您在运行时隐藏方法。输出将基于基类方法。
  • override覆盖。 - 表示您使用基类的引用调用派生类方法。输出将基于派生类方法。

答案 8 :(得分:1)

我的解释版本来自于使用属性来帮助理解差异。

override很简单,对吧?底层类型会覆盖父级

new可能是误导性的(对我而言)。通过属性,它更容易理解:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

使用调试器,您可以注意到Foo foo具有 2 GetSomething属性,因为它实际上有2个版本的属性,Foo' s和Bar&,并且知道要使用哪一个,c#" picks"当前类型的属性。

如果你想使用Bar的版本,你可以使用覆盖或改为使用Foo foo

Bar bar只有 1 ,因为它需要GetSomething的完全行为。

答案 9 :(得分:0)

不使用任何方法标记方法意味着:使用对象的编译类型而不是运行时类型(静态绑定)来绑定此方法。

virtual标记方法意味着:使用对象的运行时类型而不是编译时间类型(动态绑定)来绑定此方法。

在派生类中用virtual标记基类override方法意味着:这是使用对象的运行时类型进行绑定的方法(动态绑定)。

在派生类中用virtual标记基类new方法的意思是:这是一个新方法,与基类中同名的方法无关,应该绑定使用对象的编译时类型(静态绑定)。

未在派生类中标记基类virtual方法意味着:该方法被标记为new(静态绑定)。

标记方法abstract的意思是:该方法是虚拟的,但是我不会为其声明主体,并且其类也是抽象的(动态绑定)。

答案 10 :(得分:0)

using System;  
using System.Text;  
  
namespace OverrideAndNew  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            BaseClass bc = new BaseClass();  
            DerivedClass dc = new DerivedClass();  
            BaseClass bcdc = new DerivedClass();  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in BaseClass.  
            bc.Method1();  
            bc.Method2();  
            // Output:  
            // Base - Method1  
            // Base - Method2  
  
            // The following two calls do what you would expect. They call  
            // the methods that are defined in DerivedClass.  
            dc.Method1();  
            dc.Method2();  
            // Output:  
            // Derived - Method1  
            // Derived - Method2  
  
            // The following two calls produce different results, depending
            // on whether override (Method1) or new (Method2) is used.  
            bcdc.Method1();  
            bcdc.Method2();  
            // Output:  
            // Derived - Method1  
            // Base - Method2  
        }  
    }  
  
    class BaseClass  
    {  
        public virtual void Method1()  
        {  
            Console.WriteLine("Base - Method1");  
        }  
  
        public virtual void Method2()  
        {  
            Console.WriteLine("Base - Method2");  
        }  
    }  
  
    class DerivedClass : BaseClass  
    {  
        public override void Method1()  
        {  
            Console.WriteLine("Derived - Method1");  
        }  
  
        public new void Method2()  
        {  
            Console.WriteLine("Derived - Method2");  
        }  
    }  
}