虚拟,覆盖,新和密封覆盖之间的区别

在OOP的一些概念之间我很困惑: virtualoverridenewsealed override 。 任何人都可以解释不同之处

我很清楚,如果要使用派生类方法,可以使用override关键字,以便基类方法将被派生类重写。 但我不确定new sealed override

virtual关键字用于修改方法,属性,索引器或事件声明,并允许在派生类中重写它。 例如,这个方法可以被任何inheritance它的类所覆盖:使用new修饰符显式地隐藏从基类inheritance的成员。 要隐藏inheritance的成员,请使用相同名称在派生类中声明它,并使用新修饰符对其进行修改。

这与多态性有关。 当在引用上调用虚拟方法时,引用引用的对象的实际types将用于决定使用哪种方法实现。 在派生类中重写基类的方法时,即使调用代码没有“知道”该对象是派​​生类的实例,也会使用派生类中的版本。 例如:

 public class Base { public virtual void SomeMethod() { } } public class Derived : Base { public override void SomeMethod() { } } ... Base d = new Derived(); d.SomeMethod(); 

最终会调用Derived.SomeMethod,如果覆盖Base.SomeMethod。

现在,如果使用new关键字而不是override ,则派生类中的方法不会覆盖基类中的方法,而只是隐藏它。 在这种情况下,像这样的代码:

 public class Base { public virtual void SomeOtherMethod() { } } public class Derived : Base { public new void SomeOtherMethod() { } } ... Base b = new Derived(); Derived d = new Derived(); b.SomeOtherMethod(); d.SomeOtherMethod(); 

首先会调用Base.SomeOtherMethod,然后是Derived.SomeOtherMethod。 它们实际上是两个完全分离的方法,它们碰巧具有相同的名称,而不是派生方法重写基方法。

如果你没有指定新的或者覆盖,结果输出和你指定的新输出是一样的,但是你也会得到一个编译器警告(因为你可能不知道你隐藏了一个基类的方法方法,或者实际上你可能想重写它,只是忘了包含关键字)。

重写属性声明可能包括密封修饰符。 使用此修饰符可防止派生类进一步覆盖该属性。 密封财产的访问者也被密封。

任何方法都可以被覆盖(= virtual )。 这个决定是由谁定义的方法:

 class Person { // this one is not overridable (not virtual) public String GetPersonType() { return "person"; } // this one is overridable (virtual) public virtual String GetName() { return "generic name"; } } 

现在你可以覆盖那些被覆盖的方法:

 class Friend : Person { public Friend() : this("generic name") { } public Friend(String name) { this._name = name; } // override Person.GetName: public override String GetName() { return _name; } } 

但是你不能覆盖GetPersonType方法,因为它不是虚拟的。

我们来创build这两个类的两个实例:

 Person person = new Person(); Friend friend = new Friend("Onotole"); 

当非虚方法GetPersonTypeFiend实例调用时,它实际上是Person.GetPersonType被调用的:

 Console.WriteLine(friend.GetPersonType()); // "person" 

Friend实例调用虚方法GetName ,它被称为Friend.GetName

 Console.WriteLine(friend.GetName()); // "Onotole" 

Person方法调用虚方法GetName ,它就是Person.GetName

 Console.WriteLine(person.GetName()); // "generic name" 

当调用非虚方法时,方法体不被查找 – 编译器已经知道需要调用的实际方法。 而对于虚拟方法,编译器不能确定要调用哪一个,并且在运行时在类层次结构中从下往上查找,从该方法调用的实例types开始:for friend.GetName它看起来在Friend类中find它,对于person.GetName类,它从Person开始并在那里find它。

有时你创build一个子类,重写一个虚拟方法,并且你不希望在层次结构中有更多的覆盖 – 你使用sealed override (说你是最后一个覆盖方法):

 class Mike : Friend { public sealed override String GetName() { return "Mike"; } } 

但有时候,你的朋友Mike决定改变他的性别,因此他的名字改成Alice。你可以改变原来的代码,或者改成Mike的子类:

 class Alice : Mike { public new String GetName() { return "Alice"; } } 

在这里你创build一个完全不同的方法(现在你有两个)。 哪种方法和何时被调用? 这取决于你如何称呼它:

 Alice alice = new Alice(); Console.WriteLine(alice.GetName()); // the new method is called, printing "Alice" Console.WriteLine(((Mike)alice).GetName()); // the method hidden by new is called, printing "Mike" 

当你从Alice的angular度打电话给Alice.GetName时候,你可以打电话给Mike.GetName 。 这里没有运行时查找 – 因为这两种方法都是非虚拟的。

你总是可以创buildnew方法 – 你隐藏的方法是否是虚拟的。

这也适用于属性和事件 – 它们被表示为下面的方法。

默认情况下,一个方法不能在派生类中重写,除非声明为virtualabstractvirtual意味着在调用之前检查更新的实现abstract意味着相同,但是保证在所有派生类中被覆盖。 另外,基类中不需要实现,因为它将在别处被重新定义。

上面的例外是new修饰符。 未声明为virtualabstract可以使用派生类中的new修饰符重新定义。 当在基类中调用该方法时执行基方法,并且在派生类中调用该方法时,将执行新方法。 所有new关键字允许你做的是在类层次结构中有两个同名的方法。

最后,一个sealed修饰符打破了virtual方法的链条,使它们不能再被覆盖。 这不是经常使用,但选项在那里。 从前一个派生出来的3个类的链条更有意义

 A -> B -> C 

如果A有一个virtualabstract方法,在B中被overridden ,那么它也可以通过声明它被sealedB来防止C再次改变它。

sealed也用在classes ,那就是你经常会遇到这个关键字的地方。

我希望这有帮助。

  public class Base { public virtual void SomeMethod() { Console.WriteLine("B"); } } //This one is Simple method public class Derived : Base { public void SomeMethod() { Console.WriteLine("D"); } //This method has 'new' keyword public new void SomeMethod() { Console.WriteLine("D"); } //This method has 'override' keyword public override void SomeMethod() { Console.WriteLine("D"); } } 

现在第一件事首先

  Base b=new Base(); Derived d=new Derived(); b.SomeMethod(); will always write B d.SomeMethod(); will always write D 

现在关键字都是关于多态性的

  Base b = new Derived(); 
  1. 在基类中使用virtual并在Derived重写会给D(多态性)。
  2. Base使用不带virtual override会给出错误。
  3. 类似的,写一个方法(没有覆盖)与virtual将写入'B'的警告(因为没有多态性完成)。
  4. 要隐藏这样的警告,就像在上面那样在Derived写入new简单方法之前。
  5. new关键字是另一个故事,它只是隐藏了警告,告诉同名的属性在基类中。
  6. 除了新的修改器, virtual或者new都是一样的

  7. newoverride不能在同一个方法或属性之前使用。

  8. 在任何类或方法locking在Derived类中使用之前sealed ,并给出编译时错误。