繁体   English   中英

virtual、override、new和sealed override的区别

[英]The difference between virtual, override, new and sealed override

我对 OOP 的一些概念感到很困惑: virtualoverridenew和 seal sealed override 谁能解释这些差异?

我很清楚,如果要使用派生的 class 方法,可以使用override关键字,这样基本的 class 方法将被派生的 class 覆盖。 但我不确定newsealed 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 中覆盖方法,除非它被声明为virtualabstract virtual表示在调用之前检查较新的实现abstract表示相同,但保证在所有派生类中被覆盖。 此外,在基本 class 中不需要实现,因为它将在其他地方重新定义。

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

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

A -> B -> C

如果A有一个在B中被overriddenvirtualabstract方法,那么它还可以通过在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. 在基础 class 中使用virtual并在Derived中覆盖将给出 D(多态性)。
  2. Base中使用没有virtualoverride会出错。
  3. 同样,使用virtual编写方法(无覆盖)将写入带有警告的“B”(因为没有完成多态性)。
  4. 要隐藏上述警告,请在Derived中的那个简单方法之前编写new
  5. new关键字是另一回事,它只是隐藏了警告,告诉您在基础 class 中存在同名属性。
  6. virtualnew两者都相同,除了new 修饰符

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

  8. 在任何 class 之前sealed或方法将其锁定以用于派生 class 并给出编译时错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM