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

本文关键字:覆盖 密封 之间 区别 新覆盖 虚拟 | 更新日期: 2023-09-27 17:58:04

我很困惑OOP的一些概念:virtualoverridenewsealed override。有人能解释其中的区别吗?

我很清楚,如果要使用派生类方法,可以使用override关键字,这样基类方法就会被派生类覆盖。但我不确定newsealed override

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

virtual关键字用于修改方法、属性、索引器或事件声明,并允许在派生类中重写它。例如,此方法可以由继承它的任何类重写:使用新修饰符可以显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同的名称在派生类中声明该成员,然后使用新的修饰符对其进行修改。

这一切都与多态性有关。当对引用调用虚拟方法时,将使用引用引用的对象的实际类型来决定使用哪个方法实现。当基类的方法在派生类中被重写时,即使调用代码不"知道"该对象是派生类的实例,也会使用派生类中的版本。例如:

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。它们实际上是两个完全独立的方法,碰巧具有相同的名称,而不是覆盖基方法的派生方法。

如果没有指定new或重写,则结果输出与指定new相同,但也会收到编译器警告(因为您可能没有意识到您在基类方法中隐藏了一个方法,或者实际上您可能想要重写它,只是忘记了包含关键字)。

重写属性声明可能包括密封修饰符。使用此修饰符可防止派生类进一步重写属性。密封属性的访问器也是密封的。

任何方法都可以被重写(=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");

Friend实例调用非虚拟方法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"

当调用非虚拟方法时,不会查找方法体——编译器已经知道需要调用的实际方法。而对于虚拟方法,编译器无法确定要调用哪一个,并且在运行时会在类层次结构中从下到上从调用该方法的实例类型开始查找:对于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方法。

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

默认情况下,除非声明为virtualabstract,否则不能在派生类中重写方法。virtual表示在调用之前检查较新的实现,abstract表示相同,但保证在所有派生类中都被重写。此外,基类中不需要实现,因为它将在其他地方重新定义。

上述情况的例外是new修饰符。未声明为virtualabstract的方法可以在派生类中使用new修饰符重新定义。当在基类中调用该方法时,执行基方法,当在派生类中调用时,执行新方法。new关键字允许您在类层次结构中使用两个名称相同的方法

最后,sealed修饰符破坏了virtual方法的链,使它们不再可重写。这并不经常使用,但选项是存在的。它更有意义的是,有一个由3个类组成的链,每个类都源于前一个

A -> B -> C

如果A具有virtualabstract方法,即B中的overridden,则它还可以通过在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();
  1. 在基类中使用CCD_ 50,在CCD_
  2. Base中使用override而不使用virtual会产生错误
  3. 类似地,用virtual写一个方法(没有重写)会写带有警告的"B"(因为没有执行多态性)
  4. 要隐藏上述警告,请在Derived中的简单方法之前写入new
  5. new关键字是另一回事,它只是隐藏了告诉基类中存在同名属性的警告
  6. virtualnew除了新改性剂

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

  8. 在任何类或方法锁定它以在派生类中使用之前的sealed,它会给出编译时错误