C# – 关键字使用虚拟+覆盖与新

在基本类型“ virtual ”中声明一个方法,然后在使用“ override ”关键字的子类型中覆盖它,而不是在声明子类型中的匹配方法时简单地使用“ 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 ()); } } 

这打印错误,如果你使用覆盖,它会打印真实。

(基本代码取自Joseph Daigle)

所以,如果你正在做真正的多态,你应该总是忽略 。 唯一需要使用“新”的地方是该方法与基类版本没有任何关系。

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

再次,采取约瑟夫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

虚拟/覆盖说明

下面是一些代码来理解虚拟和非虚拟方法的不同之处:

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

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

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

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

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

new告诉编译器,你正在向派生类中添加一个方法,这个派生类和基类中的一个方法具有相同的名字,但是它们之间没有任何关系。

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

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

 (new SubClass()).NewBar() 

将调用SubClass的NewBar()方法。

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

override关键字和new关键字的区别在于前者是方法重写,后者是方法隐藏。

查看下面的链接了解更多信息…

MSDN和其他

  • new关键字是隐藏。 – 意味着你在运行时隐藏了你的方法。 输出将基于基类的方法。
  • override重写。 – 意味着你正在调用你的派生类的方法引用基类。 输出将基于派生类的方法。

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

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 foo2个 GetSomething属性,因为它实际上有2个版本的属性FooBar ,要知道使用哪一个属性,C#“挑选”当前类型的属性。

如果你想使用Bar的版本,你可以使用override或者使用Foo foo

Bar bar只有1 ,因为它希望GetSomething 全新行为。