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

问题描述 投票:72回答:4

我对OOP的一些概念感到困惑:virtualoverridenewsealed override。谁能解释这些差异?

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

c# oop override virtual new-operator
4个回答
101
投票

virtual关键字用于修改方法,属性,索引器或事件声明,并允许在派生类中重写它。例如,任何继承它的类都可以覆盖此方法:使用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相同,但您也会收到编译器警告(因为您可能不知道您在基类中隐藏了一个方法)方法,或者你可能想要覆盖它,只是忘了包含关键字)。

覆盖的属性声明可能包括sealed修饰符。使用此修饰符可防止派生类进一步覆盖该属性。密封财产的访问者也是密封的。


35
投票

任何方法都可以覆盖(= 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";
    }
}

但有时你的朋友迈克决定改变他的性别,因此他的名字改为爱丽丝:)你可以改变原始代码或改为亚迈克:

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方法 - 您隐藏的方法是否为虚拟方法。

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


17
投票

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

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

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

A -> B -> C

如果Avirtualabstract方法,即overridden中的B,那么它也可以通过在C中声明sealed来阻止B再次改变它。

sealed也用于classes,这是您通常会遇到此关键字的地方。

我希望这有帮助。


8
投票
 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. 在基类中使用virtual并在Derived中覆盖将给出D(多态)。
  2. override中使用没有virtualBase会出错。
  3. 类似地,用virtual编写一个方法(没有覆盖)会写'B'并发出警告(因为没有完成多态)。
  4. 隐藏这样的警告,如上面的点在new中的那个简单方法之前写Derived
  5. new关键字是另一个故事,它只是隐藏警告,告诉基类中有相同名称的属性。
  6. virtual外,newnew modifier都是相同的
  7. newoverride不能在相同的方法或财产之前使用。
  8. sealed在任何类或方法锁定它以在Derived类中使用之前它会产生编译时错误。
© www.soinside.com 2019 - 2024. All rights reserved.