Unterschied zwischen neu und überschreiben


198

Ich frage mich, was der Unterschied zwischen den folgenden ist:

Fall 1: Basisklasse

public void DoIt();

Fall 1: Vererbte Klasse

public new void DoIt();

Fall 2: Basisklasse

public virtual void DoIt();

Fall 2: Vererbte Klasse

public override void DoIt();

Sowohl Fall 1 als auch Fall 2 scheinen auf der Grundlage der von mir durchgeführten Tests den gleichen Effekt zu haben. Gibt es einen Unterschied oder einen bevorzugten Weg?


2
Duplikat vieler Fragen, einschließlich stackoverflow.com/questions/159978/…
Jon Skeet

Antworten:


267

Der Überschreibungsmodifikator kann für virtuelle Methoden verwendet werden und muss für abstrakte Methoden verwendet werden. Dies gibt an, dass der Compiler die zuletzt definierte Implementierung einer Methode verwenden soll. Selbst wenn die Methode für einen Verweis auf die Basisklasse aufgerufen wird, wird die sie überschreibende Implementierung verwendet.

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

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

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

wird aufrufen, Derived.DoItwenn dies überschreibt Base.DoIt.

Der neue Modifikator weist den Compiler an, Ihre untergeordnete Klassenimplementierung anstelle der übergeordneten Klassenimplementierung zu verwenden. Jeder Code, der nicht auf Ihre Klasse verweist, sondern auf die übergeordnete Klasse, verwendet die Implementierung der übergeordneten Klasse.

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

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Ich rufe Base.DoItdann zuerst an Derived.DoIt. 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.

Quelle: Microsoft-Blog


5
This indicates for the compiler to use the last defined implementation of a method. Wie kann die zuletzt definierte Implementierung einer Methode gefunden werden?
AminM

5
Beginnen Sie mit einer konkreten Klasse und prüfen Sie, ob die interessierende Methode implementiert ist. Wenn ja, sind Sie fertig. Wenn dies nicht der Fall ist, gehen Sie in der Vererbungshierarchie einen Schritt nach oben, dh prüfen Sie, ob die Superklasse über die interessierende Methode verfügt. Fahren Sie fort, bis Sie die gewünschte Methode gefunden haben.
csoltenborn

2
Beachten Sie auch, dass Sie overrideeine Methode nur verwenden können, wenn die Basisklasse die Methode als definiert virtual. Das Wort virtualist die Basisklasse, die sagt: "Hey, wenn ich diese Methode aufrufe, könnte sie praktisch durch eine abgeleitete Implementierung ersetzt worden sein, daher weiß ich nicht im Voraus, welche Methodenimplementierung ich zur Laufzeit tatsächlich virtualaufrufe ein Platzhalter für eine Methode. Dies bedeutet, dass Methoden, die nicht als markiert virtualsind, nicht überschrieben werden können. Sie können jedoch jede nicht virtuelle Methode in einer abgeleiteten Klasse durch den Modifikator ersetzennew , auf den nur auf abgeleiteter Ebene
zugegriffen werden kann

177

virtuell : Gibt an, dass eine Methode möglicherweise von einem Erben überschrieben wird

überschreiben : Überschreibt die Funktionalität einer virtuellen Methode in einer Basisklasse und bietet unterschiedliche Funktionen.

neu : verbirgt die ursprüngliche Methode (die nicht virtuell sein muss) und bietet unterschiedliche Funktionen. Dies sollte nur verwendet werden, wenn dies unbedingt erforderlich ist.

Wenn Sie eine Methode ausblenden, können Sie weiterhin auf die ursprüngliche Methode zugreifen, indem Sie das Casting in die Basisklasse durchführen. Dies ist in einigen Szenarien nützlich, aber gefährlich.


2
Warum ist es gefährlich, eine Methode zu verwenden, die die Basismethode verbirgt? Oder implizieren Sie, dass das Casting im Allgemeinen gefährlich ist?
Mark

3
@Mark - Ein Anrufer ist sich der Implementierung möglicherweise nicht bewusst, was zu versehentlichem Missbrauch führt.
Jon B

Können Sie overrideund / oder newohne virtualdie übergeordnete Methode verwenden?
Aaron Franke

16

Im ersten Fall verstecken Sie die Definition in der übergeordneten Klasse. Dies bedeutet, dass es nur aufgerufen wird, wenn Sie das Objekt als untergeordnete Klasse behandeln. Wenn Sie die Klasse in ihren übergeordneten Typ umwandeln, wird die übergeordnete Methode aufgerufen. In der zweiten Instanz wird die Methode überschrieben und unabhängig davon aufgerufen, ob das Objekt als untergeordnete oder übergeordnete Klasse umgewandelt wird.


7

Versuchen Sie Folgendes: (case1)

((BaseClass)(new InheritedClass())).DoIt()

Bearbeiten: Virtual + Override werden zur Laufzeit aufgelöst (Override überschreibt also wirklich virtuelle Methoden), während New nur eine neue Methode mit demselben Namen erstellt und die alte verbirgt, wird sie zur Kompilierungszeit aufgelöst -> Ihr Compiler nennt die Methode it ' sieht '


3

In Fall 1, wenn Sie die DoIt () -Methode der geerbten Klasse aufgerufen haben, während der Typ als Basisklasse deklariert ist, wird die Aktion der Basisklasse sogar angezeigt.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

Könnten Sie die Warnung oder den Fehler, den Sie erhalten, posten? Dieser Code hat gut funktioniert, als ich ihn ursprünglich gepostet habe.
Matthew Whited

Dies sollte alles in Ihre Einstiegspunktklasse (Programm) eingefügt werden. Das wurde entfernt, um eine bessere Formatierung auf dieser Site zu ermöglichen.
Matthew Whited

3

Der Unterschied zwischen den beiden Fällen besteht darin, dass in Fall 1 die Basismethode DoItnicht überschrieben, sondern nur ausgeblendet wird. Dies bedeutet, dass abhängig vom Typ der Variablen davon abhängt, welche Methode aufgerufen wird. Beispielsweise:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Dies kann sehr verwirrend sein und zu unerwartetem Verhalten führen und sollte nach Möglichkeit vermieden werden. Der bevorzugte Weg wäre also Fall 2.


3
  • newbedeutet, dass Sie Ihren REFERENCE-Typ (links von =) respektieren und dabei die Methode der Referenztypen ausführen. Wenn die neu definierte Methode kein newSchlüsselwort hat, verhält sie sich so wie sie ist. Darüber hinaus ist es auch als nicht polymorphe Vererbung bekannt . Das heißt: "Ich mache eine brandneue Methode in der abgeleiteten Klasse, die absolut nichts mit gleichnamigen Methoden in der Basisklasse zu tun hat." - Von besagtem Whitaker
  • override, das mit dem virtualSchlüsselwort in seiner Basisklasse verwendet werden muss, bedeutet, dass Sie Ihren OBJECT-Typ (rechts von =) respektieren und dabei die Methodenüberschreibung unabhängig vom Referenztyp ausführen. Darüber hinaus ist es auch als polymorphe Vererbung bekannt .

Meine Art, beide Schlüsselwörter zu berücksichtigen, dass sie sich gegenüberstehen.

overrideDas virtualSchlüsselwort : muss definiert werden, um die Methode zu überschreiben. Die Methode mit dem overrideSchlüsselwort, dass unabhängig vom Referenztyp (Referenz der Basisklasse oder abgeleiteten Klasse) die Methode der Basisklasse ausgeführt wird, wenn sie mit der Basisklasse instanziiert wird. Andernfalls wird die Methode der abgeleiteten Klasse ausgeführt.

new: Wenn das Schlüsselwort von einer Methode verwendet wird, ist im Gegensatz zum overrideSchlüsselwort der Referenztyp wichtig. Wenn es mit einer abgeleiteten Klasse instanziiert wird und der Referenztyp Basisklasse ist, wird die Methode der Basisklasse ausgeführt. Wenn es mit einer abgeleiteten Klasse instanziiert wird und der Referenztyp eine abgeleitete Klasse ist, wird die Methode der abgeleiteten Klasse ausgeführt. Es ist nämlich der Kontrast des overrideSchlüsselworts. Wenn Sie vergessen oder unterlassen, der Methode ein neues Schlüsselwort hinzuzufügen, verhält sich der Compiler standardmäßig so, wie das newSchlüsselwort verwendet wird.

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Rufen Sie in main:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Ausgabe:

A
B
B
base test
derived test

Neues Codebeispiel,

Spielen Sie mit dem Code, indem Sie ihn einzeln kommentieren.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

Wenn das Schlüsselwort overridein der Ableitungsklasse verwendet wird, überschreibt es die übergeordnete Methode.

Wenn das Schlüsselwort newin der Ableitungsklasse verwendet wird, wird die von der übergeordneten Methode ausgeblendete Methode abgeleitet.


1

Ich hatte die gleiche Frage und es ist wirklich verwirrend. Sie sollten berücksichtigen, dass Override und neue Schlüsselwörter nur mit Objekten vom Typ Basisklasse und Wert der abgeleiteten Klasse funktionieren. In diesem Fall wird nur werden Sie die Wirkung von Überschreibung und neu sehen: Also , wenn Sie class Aund B, Berbt von A, dann instanziiert Sie ein Objekt wie folgt aus :

A a = new B();

Beim Aufruf von Methoden wird nun der Status berücksichtigt. Überschreiben : bedeutet, dass die Funktion der Methode erweitert wird und dann die Methode in der abgeleiteten Klasse verwendet wird, während new den Compiler anweist, die Methode in der abgeleiteten Klasse auszublenden und stattdessen die Methode in der Basisklasse zu verwenden. Hier ist ein sehr guter Anblick zu diesem Thema:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

Der folgende Artikel ist in vb.net, aber ich denke, die Erklärung zu neuen oder Überschreibungen ist sehr einfach zu verstehen.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

Irgendwann im Artikel gibt es diesen Satz:

Im Allgemeinen geht Shadows davon aus, dass die dem Typ zugeordnete Funktion aufgerufen wird, während Overrides davon ausgeht, dass die Objektimplementierung ausgeführt wird.

Die akzeptierte Antwort auf diese Frage ist perfekt, aber ich denke, dieser Artikel bietet gute Beispiele, um die Unterschiede zwischen diesen beiden Schlüsselwörtern besser zu verstehen.


1

Von all diesen neu am verwirrendsten. Durch das Experimentieren gibt das neue Schlüsselwort Entwicklern die Möglichkeit, die Implementierung der geerbten Klasse mit der Implementierung der Basisklasse zu überschreiben, indem der Typ explizit definiert wird. Es ist wie umgekehrt zu denken.

Im folgenden Beispiel gibt das Ergebnis "Abgeleitetes Ergebnis" zurück, bis der Typ explizit als BaseClass-Test definiert ist. Erst dann wird "Basisergebnis" zurückgegeben.

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
Fügen Sie Ihren Kommentar hinzu, wenn Sie Einwände erheben. Hit and Run ist so feige.
nützlichBee

0

Der Funktionsunterschied wird in diesen Tests nicht gezeigt:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

In diesem Beispiel ist das aufgerufene Doit dasjenige, von dem Sie erwarten, dass es aufgerufen wird.

Um den Unterschied zu erkennen, müssen Sie Folgendes tun:

BaseClass obj = new DerivedClass();

obj.DoIt();

Sie werden sehen, wenn Sie diesen Test ausführen, dass in Fall 1 (wie Sie ihn definiert haben) das DoIt()In BaseClassaufgerufen wird, in Fall 2 (wie Sie es definiert haben) das DoIt()In DerivedClassaufgerufen wird.


-1

Im ersten Fall wird die DoIt () -Methode der abgeleiteten Klasse aufgerufen, da das neue Schlüsselwort die DoIt () -Methode der Basisklasse verbirgt.

Im zweiten Fall wird überschriebenes DoIt () aufgerufen.

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

Lassen Sie eine Instanz dieser Klassen erstellen

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Alles wird oben erwartet. Setzen Sie instanceB und instanceC auf instanceA und rufen Sie die DoIt () -Methode auf und überprüfen Sie das Ergebnis.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt (); würde Ihnen C :: DoIt () geben, nicht B :: DoIt ()
BYS2

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.