[英]The difference between virtual, override, new and sealed override
我对 OOP 的一些概念感到很困惑: virtual
, override
, new
和 seal sealed override
。 谁能解释这些差异?
我很清楚,如果要使用派生的 class 方法,可以使用override
关键字,这样基本的 class 方法将被派生的 class 覆盖。 但我不确定new
和sealed override
。
virtual关键字用于修改方法、属性、索引器或事件声明,并允许在派生的 class 中覆盖它。 例如,此方法可以被任何继承它的 class 覆盖:使用 new 修饰符显式隐藏从基 class 继承的成员。 要隐藏继承的成员,请在派生的 class 中使用相同的名称声明它,并使用 new 修饰符对其进行修改。
这都与多态性有关。 当在引用上调用虚拟方法时,引用所引用的 object 的实际类型用于决定使用哪个方法实现。 When a method of a base class is overridden in a derived class, the version in the derived class is used, even if the calling code didn't "know" that the object was an instance of the derived class. 例如:
public class Base
{
public virtual void SomeMethod()
{
}
}
public class Derived : Base
{
public override void SomeMethod()
{
}
}
...
Base d = new Derived();
d.SomeMethod();
如果覆盖 Base.SomeMethod,最终将调用 Derived.SomeMethod。
现在,如果您使用new关键字而不是override ,则派生 class 中的方法不会覆盖基本 class 中的方法,它只是隐藏它。 在这种情况下,代码如下:
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。 它们实际上是两个完全独立的方法,它们恰好具有相同的名称,而不是覆盖基本方法的派生方法。
如果您未指定 new 或 overrides,则生成的 output 与您指定 new 时相同,但您也会收到编译器警告(因为您可能不知道您在基础 class 中隐藏了一个方法方法,或者实际上您可能想要覆盖它,只是忘记包含关键字)。
覆盖的属性声明可能包含sealed修饰符。 使用此修饰符可防止派生的 class 进一步覆盖该属性。 密封属性的访问者也被密封。
任何方法都可以是可覆盖的(= 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
方法,因为它不是虚拟的。
让我们创建这些类的两个实例:
Person person = new Person();
Friend friend = new Friend("Onotole");
当Fiend
实例调用非虚拟方法GetPersonType
时,实际上调用的是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"
当调用非虚拟方法时,不会查找方法体 - 编译器已经知道需要调用的实际方法。 而虚拟方法编译器无法确定调用哪一个,并且在运行时在 class 层次结构中从下到上从调用该方法的实例类型开始查找:对于friend.GetName
,它看起来开始在Friend
class 并立即找到它,对于person.GetName
class 它从Person
开始并在那里找到它。
有时您创建一个子类,覆盖一个虚拟方法并且您不希望在层次结构中再有任何覆盖 - 为此您使用sealed override
(假设您是最后一个覆盖该方法的人):
class Mike : Friend
{
public sealed override String GetName()
{
return "Mike";
}
}
但有时你的朋友 Mike 决定改变他的性别,因此把他的名字改成 Alice :) 你可以改变原始代码,或者改为子类 Mike:
class Alice : Mike
{
public new String GetName()
{
return "Alice";
}
}
在这里,您创建了一个完全不同的具有相同名称的方法(现在您有两个)。 调用哪种方法以及何时调用? 这取决于你如何称呼它:
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
的角度调用它时,您调用Alice.GetName
,当从Mike
的角度调用时,您调用Mike.GetName
。 这里没有进行运行时查找 - 因为这两种方法都是非虚拟的。
您始终可以创建new
方法 - 无论您隐藏的方法是否为虚拟方法。
这也适用于属性和事件——它们在下面表示为方法。
默认情况下,不能在派生的 class 中覆盖方法,除非它被声明为virtual
或abstract
。 virtual
表示在调用之前检查较新的实现, abstract
表示相同,但保证在所有派生类中被覆盖。 此外,在基本 class 中不需要实现,因为它将在其他地方重新定义。
上述情况的例外是new
修饰符。 可以在派生的 class 中使用new
修饰符重新定义未声明为virtual
或abstract
的方法。 当在基础 class 中调用该方法时,将执行基础方法,而在派生 class 中调用该方法时,将执行新方法。 所有new
关键字允许您在 class 层次结构中使用两个具有相同名称的方法。
最后一个sealed
的修饰符打破了virtual
方法链,使它们不再被覆盖。 这不经常使用,但选项在那里。 使用 3 个类的链更有意义,每个类都派生自前一个类
A -> B -> C
如果A
有一个在B
中被overridden
的virtual
或abstract
方法,那么它还可以通过在B
中声明它sealed
防止C
再次更改它。
sealed
也用在classes
中,这就是你经常会遇到这个关键字的地方。
我希望这有帮助。
public class Base
{
public virtual void SomeMethod()
{
Console.WriteLine("B");
}
}
public class Derived : Base
{
//Same method is written 3 times with different keywords to explain different behaviors.
//This one is Simple method
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
关键字是另一回事,它只是隐藏了警告,告诉您在基础 class 中存在同名属性。 virtual
或new
两者都相同,除了new 修饰符
new
和override
不能在相同的方法或属性之前使用。
sealed
或方法将其锁定以用于派生 class 并给出编译时错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.