Der Unterschied zwischen virtueller, Override-, neuer und versiegelter Override


79

Ich bin ziemlich zwischen einigen Konzepte der OOP verwirrt: virtual, override, newund sealed override. Kann jemand die Unterschiede erklären?

Ich bin mir ziemlich sicher, dass man das overrideSchlüsselwort verwenden kann, wenn die abgeleitete Klassenmethode verwendet werden soll, damit die Basisklassenmethode von der abgeleiteten Klasse überschrieben wird. Aber ich bin mir nicht sicher new, und sealed override.

Antworten:


107

Das virtuelle Schlüsselwort wird verwendet, um eine Methode, eine Eigenschaft, einen Indexer oder eine Ereignisdeklaration zu ändern und das Überschreiben in einer abgeleiteten Klasse zu ermöglichen. Diese Methode kann beispielsweise von jeder Klasse überschrieben werden, die sie erbt: Verwenden Sie den neuen Modifikator, um ein von einer Basisklasse geerbtes Mitglied explizit auszublenden. Um ein geerbtes Mitglied auszublenden, deklarieren Sie es in der abgeleiteten Klasse mit demselben Namen und ändern Sie es mit dem neuen Modifikator.

Das hat alles mit Polymorphismus zu tun. Wenn eine virtuelle Methode für eine Referenz aufgerufen wird, wird anhand des tatsächlichen Typs des Objekts, auf das sich die Referenz bezieht, entschieden, welche Methodenimplementierung verwendet werden soll. Wenn eine Methode einer Basisklasse in einer abgeleiteten Klasse überschrieben wird, wird die Version in der abgeleiteten Klasse verwendet, auch wenn der aufrufende Code nicht "wusste", dass das Objekt eine Instanz der abgeleiteten Klasse ist. Zum Beispiel:

public class Base
{
  public virtual void SomeMethod()
  {
  }
}

public class Derived : Base
{
  public override void SomeMethod()
  {
  }
}

...

Base d = new Derived();
d.SomeMethod();

wird am Ende Derived.SomeMethod aufrufen, wenn dies Base.SomeMethod überschreibt.

Wenn Sie jetzt das neue Schlüsselwort anstelle des Überschreibens verwenden , überschreibt die Methode in der abgeleiteten Klasse die Methode in der Basisklasse nicht, sondern verbirgt sie lediglich. In diesem Fall Code wie folgt:

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();

Ruft zuerst Base.SomeOtherMethod und dann Derived.SomeOtherMethod auf. Es handelt sich effektiv um zwei völlig getrennte Methoden, die zufällig denselben Namen haben, und nicht um die abgeleitete Methode, die die Basismethode überschreibt.

Wenn Sie weder neu noch Überschreibungen angeben, ist die resultierende Ausgabe dieselbe wie bei Angabe von neu, es wird jedoch auch eine Compiler-Warnung angezeigt (da Sie möglicherweise nicht wissen, dass Sie eine Methode in der Basisklasse ausblenden Methode, oder vielleicht wollten Sie sie überschreiben und haben nur vergessen, das Schlüsselwort anzugeben).

Eine übergeordnete Eigenschaftsdeklaration kann den versiegelten Modifikator enthalten. Die Verwendung dieses Modifikators verhindert, dass eine abgeleitete Klasse die Eigenschaft weiter überschreibt. Die Accessoren eines versiegelten Eigentums sind ebenfalls versiegelt.


Danke für die Eingabe. Aber eines fällt mir nicht ein: Was ist die Verwendung von Base b = new Derived ()? Ist dies ein Objekt der Basisklasse oder der abgeleiteten Klasse?
Xorpower

2
Abgeleitete Klasse. Ich denke, man muss sich mehr mit Polymorphismus befassen. Hier ist eine gute für Ihre Lektüre. msdn.microsoft.com/en-us/library/ms173152(v=vs.80).aspx
CharithJ

5
@Xor: In diesem Fall erstellen Sie eine Instanz eines DerivedObjekts und speichern die Referenz in einer BaseVariablen. Dies ist gültig, weil ein DerivedObjekt auch ein Objekt ist Base. Das ist so, als würde man sagen, dass wir eine "Person" brauchen, damit wir "Johnny" bekommen, der zufällig eine Person ist. Gleiches Angebot hier.
Jeff Mercado

Ich möchte hier nur einen Punkt hinzufügen. Base b = new Derived()Es besagt, dass auf eine BaseKlasse durch Derived classReferenz zugegriffen werden kann, da a eine derived classSpezialisierung ihrer Basisklasse ist. DerivedKlassen können alle Operationen ausführen (z. B. Aufrufen von Basisklassenmethoden usw. ), die a ausführen base classkann. Aber a Base classkann die Operationen, die es ausführen kann, nicht ausführen Derived class. Also Derived d = new Base()nicht richtig, aber Base b = new Derived()richtig.
mmushtaq

Können Sie den Zweck der Verwendung des newModifikators erläutern hide a base class method? Im zweiten Beispiel b.SomeOtherMethod()ruft der Aufruf die Basisklassenimplementierung auf (man könnte sagen, er hat die Methode der abgeleiteten Klasse ausgeblendet ). Wenn dies ein typisches Anwendungsbeispiel ist, newscheint es verwendet zu werden, wenn der Aufrufer beabsichtigt, eine Variable von a compile-time typezu haben, um seine Methode zu verwenden, und nicht die Methode von einer runtime types, die ihm zugewiesen werden kann.
Minh Tran

35

Jede Methode kann überschreibbar sein (= virtual) oder nicht. Die Entscheidung trifft derjenige, der die Methode definiert:

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";
    }
}

Jetzt können Sie die überschreibbaren Methoden überschreiben:

class Friend : Person
{
    public Friend() : this("generic name") { }

    public Friend(String name)
    {
        this._name = name;
    }

    // override Person.GetName:
    public override String GetName()
    {
        return _name;
    }
}

Sie können die GetPersonTypeMethode jedoch nicht überschreiben , da sie nicht virtuell ist.

Erstellen wir zwei Instanzen dieser Klassen:

Person person = new Person();
Friend friend = new Friend("Onotole");

Wenn eine nicht virtuelle Methode GetPersonTypevon einer FiendInstanz aufgerufen wird, heißt sie tatsächlich Person.GetPersonType:

Console.WriteLine(friend.GetPersonType()); // "person"

Wenn die virtuelle Methode GetNamevon einer FriendInstanz Friend.GetNameaufgerufen wird, heißt sie:

Console.WriteLine(friend.GetName()); // "Onotole"

Wenn die virtuelle Methode GetNamevon einer PersonInstanz Person.GetNameaufgerufen wird, heißt sie:

Console.WriteLine(person.GetName()); // "generic name"

Wenn eine nicht virtuelle Methode aufgerufen wird, wird der Methodenkörper nicht nachgeschlagen - der Compiler kennt bereits die tatsächliche Methode, die aufgerufen werden muss. Während bei virtuellen Methoden der Compiler nicht sicher sein kann, welche aufgerufen werden soll, wird er zur Laufzeit in der Klassenhierarchie von unten nach oben ab dem Instanztyp nachgeschlagen, auf den die Methode aufgerufen wird: denn friend.GetNameer sieht ab FriendKlasse und aus findet es sofort, für den person.GetNameUnterricht beginnt es Personund findet es dort.

Manchmal erstellen Sie eine Unterklasse, überschreiben eine virtuelle Methode und möchten keine weiteren Überschreibungen in der Hierarchie - Sie verwenden dies sealed override(sagen, Sie sind der letzte, der die Methode überschreibt):

class Mike : Friend
{
    public sealed override String GetName()
    {
        return "Mike";
    }
}

Aber manchmal beschließt dein Freund Mike, sein Geschlecht und damit seinen Namen in Alice zu ändern :) Du kannst entweder den Originalcode ändern oder stattdessen die Unterklasse Mike:

class Alice : Mike
{
    public new String GetName()
    {
        return "Alice";
    }
}

Hier erstellen Sie eine völlig andere Methode mit demselben Namen (jetzt haben Sie zwei). Welche Methode und wann wird aufgerufen? Es hängt davon ab, wie Sie es nennen:

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"

Wenn Sie es aus Aliceder Perspektive anrufen Alice.GetName, rufen Sie an , wenn Sie aus der Perspektive Mikeanrufen Mike.GetName. Hier wird keine Laufzeitsuche durchgeführt, da beide Methoden nicht virtuell sind.

Sie können jederzeit newMethoden erstellen - unabhängig davon, ob die ausgeblendeten Methoden virtuell sind oder nicht.

Dies gilt auch für Eigenschaften und Ereignisse - sie werden darunter als Methoden dargestellt.


1
Es gibt keine einfache und vollständige Antwort als diese, die ich irgendwo gefunden habe. Danke Loki
Reevs

19

Standardmäßig kann eine Methode in einer abgeleiteten Klasse nur überschrieben werden, wenn sie deklariert ist virtual, oder abstract. virtualbedeutet , vor dem Aufruf nach neueren Implementierungen zu suchen und abstractbedeutet dasselbe, aber es wird garantiert in allen abgeleiteten Klassen überschrieben. Außerdem ist in der Basisklasse keine Implementierung erforderlich, da sie an anderer Stelle neu definiert wird.

Die Ausnahme ist der newModifikator. Eine Methode, die nicht deklariert ist virtualoder abstractmit dem newModifikator in einer abgeleiteten Klasse neu definiert werden kann . Wenn die Methode in der Basisklasse aufgerufen wird, wird die Basismethode ausgeführt, und wenn sie in der abgeleiteten Klasse aufgerufen wird, wird die neue Methode ausgeführt. Alle newSchlüsselwörter können Sie zu tun ist, haben zwei Methoden mit dem gleichen Namen in einer Klassenhierarchie.

Schließlich unterbricht ein sealedModifikator die virtualMethodenkette und macht sie nicht wieder überschreibbar. Dies wird nicht oft verwendet, aber die Option ist da. Es ist sinnvoller mit einer Kette von 3 Klassen, die jeweils von der vorherigen abgeleitet sind

A -> B -> C

wenn Aeine hat virtualoder abstractVerfahren, das heißt overriddenin B, dann kann es auch verhindern , dass Cdaraus wieder zu ändern , indem er erklärt , sealedin B.

sealedwird auch in verwendet classes, und dort werden Sie häufig auf dieses Schlüsselwort stoßen.

Ich hoffe das hilft.


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");
  }
}

Jetzt als erstes zuerst

 Base b=new Base();
 Derived d=new Derived();
 b.SomeMethod(); //will always write B
 d.SomeMethod(); //will always write D

Jetzt dreht sich alles um Polymorphismus

 Base b = new Derived();
  1. Die Verwendung virtualin der Basisklasse und das Überschreiben in Derivedergibt D (Polymorphismus).
  2. Die Verwendung von overridewithout virtualin Baseführt zu einem Fehler.
  3. In ähnlicher Weise wird beim Schreiben einer Methode (keine Überschreibung) mit virtual'B' mit Warnung geschrieben (da kein Polymorphismus durchgeführt wird).
  4. Um eine Warnung wie oben zu verbergen, schreiben Sie newvor dieser einfachen Methode in Derived.
  5. new Das Schlüsselwort ist eine andere Geschichte. Es verbirgt einfach die Warnung, dass die gleichnamige Eigenschaft in der Basisklasse vorhanden ist.
  6. virtualoder newbeide sind bis auf den neuen Modifikator gleich

  7. newund overridekann nicht vor derselben Methode oder Eigenschaft verwendet werden.

  8. sealed Bevor eine Klasse oder Methode gesperrt wird, um in der abgeleiteten Klasse verwendet zu werden, wird ein Fehler bei der Kompilierung angezeigt.

Entschuldigung, aber -1 wegen mehrerer Kompilierungsfehler: Methode mehrfach mit denselben Parametern deklariert, keine Anführungszeichen um Zeichenfolgen B & D ...
DeveloperDan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.