虚拟,覆盖,新和密封覆盖之间的区别
在OOP的一些概念之间我很困惑: virtual
, override
, new
和sealed 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");
当非虚方法GetPersonType
被Fiend
实例调用时,它实际上是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
方法 – 你隐藏的方法是否是虚拟的。
这也适用于属性和事件 – 它们被表示为下面的方法。
默认情况下,一个方法不能在派生类中重写,除非声明为virtual
或abstract
。 virtual
意味着在调用之前检查更新的实现 , abstract
意味着相同,但是保证在所有派生类中被覆盖。 另外,基类中不需要实现,因为它将在别处被重新定义。
上面的例外是new
修饰符。 未声明为virtual
或abstract
可以使用派生类中的new
修饰符重新定义。 当在基类中调用该方法时执行基方法,并且在派生类中调用该方法时,将执行新方法。 所有new
关键字允许你做的是在类层次结构中有两个同名的方法。
最后,一个sealed
修饰符打破了virtual
方法的链条,使它们不能再被覆盖。 这不是经常使用,但选项在那里。 从前一个派生出来的3个类的链条更有意义
A -> B -> C
如果A
有一个virtual
或abstract
方法,在B
中被overridden
,那么它也可以通过声明它被sealed
在B
来防止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();
- 在基类中使用
virtual
并在Derived
重写会给D(多态性)。 - 在
Base
使用不带virtual
override
会给出错误。 - 类似的,写一个方法(没有覆盖)与
virtual
将写入'B'的警告(因为没有多态性完成)。 - 要隐藏这样的警告,就像在上面那样在
Derived
写入new
简单方法之前。 -
new
关键字是另一个故事,它只是隐藏了警告,告诉同名的属性在基类中。 -
除了新的修改器,
virtual
或者new
都是一样的 -
new
和override
不能在同一个方法或属性之前使用。 - 在任何类或方法locking在Derived类中使用之前
sealed
,并给出编译时错误。