我很困惑OOP的一些概念之间:virtual
,override
,new
和sealed override
。谁能解释差异?
我很清楚,如果要使用派生类方法,则可以使用override
关键字,这样派生类将覆盖基类方法。但是我不确定new
,和sealed override
。
Answers:
该虚拟关键字用于修改的方法,属性,索引或事件声明,并允许它被覆盖在派生类。例如,此方法可以被任何继承该方法的类覆盖:使用new修饰符可显式隐藏从基类继承的成员。若要隐藏继承的成员,请在派生类中使用相同的名称对其进行声明,然后使用new修饰符对其进行修改。
这与多态性有关。当在引用上调用虚拟方法时,引用所引用的对象的实际类型用于决定使用哪种方法实现。当在派生类中重写基类的方法时,即使调用代码不“知道”该对象是派生类的实例,也将使用派生类中的版本。例如:
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,则派生类中的方法不会覆盖基类中的方法,而只是将其隐藏。在这种情况下,如下代码:
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也不重写,则结果输出与指定new相同,但是还会收到编译器警告(因为您可能不知道自己在基类中隐藏了一个方法方法,或者实际上您可能想覆盖它,而只是忘记了包含关键字)。
覆盖的属性声明可以包含密封的修饰符。使用此修饰符可防止派生类进一步覆盖该属性。具有密封特性的访问器也被密封。
Derived
对象的实例,并将引用存储在Base
变量中。这是有效的,因为Derived
对象也是-Base
对象。这就像在说我们需要一个“人”,以便我们碰巧是一个人的“约翰尼”。同样的交易。
Base b = new Derived()
它声明一个Base
类可以通过Derived class
引用来访问,因为aderived class
是其基类的专门化。Derived
类可以执行a可以执行的所有操作(例如,调用基类方法等)base class
。但是Base class
无法执行其Derived class
可以执行的操作。因此,这Derived d = new Base()
是不正确的,但是Base b = new Derived()
正确的。
new
修饰符的目的hide a base class method
?在第二个示例中,该调用b.SomeOtherMethod()
调用基类实现(可能会说它已经隐藏了派生类的方法)。如果这是用法的典型示例,则new
当调用者打算使用a的变量compile-time type
来使用其方法而不是runtime types
可能分配给它的任何方法时,似乎会使用它。
任何方法都可以重写(= 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");
当实例GetPersonType
调用非虚拟方法时Fiend
,实际上Person.GetPersonType
就是这样:
Console.WriteLine(friend.GetPersonType()); // "person"
当实例GetName
调用虚拟方法时,即为:Friend
Friend.GetName
Console.WriteLine(friend.GetName()); // "Onotole"
当实例GetName
调用虚拟方法时,即为:Person
Person.GetName
Console.WriteLine(person.GetName()); // "generic name"
调用非虚拟方法时,不会查找方法主体-编译器已经知道需要调用的实际方法。而使用虚拟方法的编译器无法确定要调用哪个方法,因此它会在类层次结构中的运行时从头至尾从调用该方法的实例类型开始向上friend.GetName
查找:因为它看起来始于Friend
类和立即找到它,对于person.GetName
课程,它始于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
方法-所隐藏的方法是否是虚拟的。
这也适用于属性和事件-它们表示为下面的方法。
默认情况下,除非声明了方法virtual
,否则不能在派生类中重写方法abstract
。virtual
表示在调用之前检查较新的实现,并且abstract
表示相同,但是可以保证在所有派生类中将其重写。同样,基类不需要实现,因为它将在其他地方重新定义。
上面的例外是new
修饰符。未声明virtual
或abstract
可以使用new
派生类中的修饰符重新定义的方法。在基类中调用该方法时,将执行该基方法;在派生类中调用该方法时,将执行新方法。new
您所要做的所有关键字就是在类层次结构中使用两个具有相同名称的方法。
最终,sealed
修饰符打破了virtual
方法链,并使它们不再可重写。该选项不经常使用,但是可以使用。将3个类别的链从上一个衍生而来,这更有意义
A -> B -> C
如果A
具有in中的virtual
orabstract
方法,则还可以通过在中声明它来防止再次更改它。overridden
B
C
sealed
B
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(多态性)。override
不使用virtual
inBase
将产生错误。virtual
将写有警告的“ B”(因为未完成多态性)。new
该简单方法Derived
。new
关键字是另一回事,它只是隐藏警告,告诉您基类中存在具有相同名称的属性。virtual
或new
除新修饰符外,两者都相同
new
并且override
不能在相同的方法或属性之前使用。
sealed
在任何类或方法将其锁定以在派生类中使用之前,它会给出编译时错误。