C # - Verwendung von Schlüsselwörtern virtuell + überschreiben vs. neu


204

Was sind die Unterschiede zwischen dem Deklarieren einer Methode in einem Basistyp " virtual" und dem anschließenden Überschreiben in einem untergeordneten Typ mit dem overrideSchlüsselwort " " im Gegensatz zum einfachen Verwenden des newSchlüsselworts " " beim Deklarieren der übereinstimmenden Methode im untergeordneten Typ?


3
MSDN sagt "Mit newwird ein neues Mitglied mit demselben Namen erstellt und das ursprüngliche Mitglied wird ausgeblendet, während overridedie Implementierung für ein geerbtes Mitglied erweitert wird"
AminM


Antworten:


181

Das Schlüsselwort "new" überschreibt nicht, es kennzeichnet eine neue Methode, die nichts mit der Basisklassenmethode zu tun hat.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Dies gibt false aus. Wenn Sie überschreiben verwendet haben, wird true ausgegeben.

(Basiscode von Joseph Daigle)

Wenn Sie also echten Polymorphismus betreiben, sollten Sie IMMER ÜBERSCHREIBEN . Der einzige Ort, an dem Sie "new" verwenden müssen, ist, wenn die Methode in keiner Weise mit der Basisklassenversion zusammenhängt.


Sie sollten Ihren Code überarbeiten, ich hatte die Methoden statisch gemacht (was für die Verwendung des Schlüsselworts "new" gültig ist). Aber ich entschied, dass es klarer war, Instanzmethoden zu verwenden.
Joseph Daigle

1
Danke, ich habe den "statischen" Teil verpasst. Sollte mehr Aufmerksamkeit auf die Zukunft
richten

1
Beachten Sie, dass die Zeile Foo test = new Bar () hier von entscheidender Bedeutung ist. Das Schlüsselwort new / override für die Metods bestimmt, welche Metod aufgerufen wird, wenn Sie einen Balken in eine Foo-Variable einfügen.
Thomas N

... so ist es genau das gleiche wie einfach nicht mit virtualin der Basis und overridein abgeleitet? Warum existiert es? Der Code läuft auch ohne noch new- ist er also nur lesbar?
Don Cheadle

Ihre Antwort, sehr geehrter Herr, ist ziemlich vage. new und virtual-override machen das gleiche, der einzige Unterschied ist, dass new die Methode in der übergeordneten Klasse versteckt und überschreibt. Nun, überschreibt sie. Natürlich wird false ausgegeben, da Ihr Testobjekt vom Typ Foo ist. Wenn es vom Typ Bar ist, wird true ausgegeben. Ziemlich kniffliges Beispiel.
MrSilent

228

Ich finde solche Dinge mit Bildern immer leichter verständlich:

Wiederum den Code von Joseph Daigle nehmen,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Wenn Sie dann den Code wie folgt aufrufen:

Foo a = new Bar();
a.DoSomething();

HINWEIS: Das Wichtigste ist, dass unser Objekt tatsächlich ein Objekt ist Bar, aber wir speichern es in einer Variablen vom TypFoo (dies ähnelt dem Casting).

Das Ergebnis ist dann wie folgt, je nachdem, ob Sie virtual/ overrideoder newbei der Deklaration Ihrer Klassen verwendet haben.

Erklärungsbild für Virtual / Override


3
Danke ... aber könnten Sie bitte ein wenig über das Bild oben in Bezug auf das Casting erklären, das Sie gesagt haben?
Odiseh

Ups, wenn ich vergessen habe, diese paar Zeilen zu meiner vorherigen hinzuzufügen. Kommentar: Meinst du, dass virtuell / überschreibend und nicht-vital / neu nur für das Polymorphysm-Konzept verwendet werden und wenn du einfach eine Variable deklarierst (ohne Casting), heißt das nicht? Danke noch einmal.
Odiseh

Diese Antwort ist genau das, wonach ich gesucht habe. Das einzige Problem, das ich hatte, ist, dass Flickr-Bilder an meinem Arbeitsplatz blockiert sind, so dass ich über mein Telefon darauf zugreifen musste ...
Mezoid

7
Echte Anstrengung, um die Frage zu beantworten.
iMatoria

Ich würde zustimmen, wenn Sie das Bild reparieren können? Es ist hinter der Unternehmens-Firewall blockiert ...
Jeremy Thompson

43

Hier ist ein Code, um den Unterschied im Verhalten virtueller und nicht virtueller Methoden zu verstehen:

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

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

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

Vielen Dank dafür - aber warum sollte man newdie Basismethode "verstecken", wenn einfach nicht überschrieben zu werden scheint?
Don Cheadle

1
Ich glaube nicht, dass es erstellt wurde, um zu verhindern, dass die Basisklasse überschrieben wird. Ich denke, es wurde erstellt, um Namenskonflikte zu vermeiden, da Sie eine Methode nicht überschreiben können, die es nicht ist, virtualund der Compiler sich beschwert, wenn er denselben Funktionsnamen in einer Klasse "Blutlinie" ohne virtualSignatur
sieht

19

Das newSchlüsselwort erstellt tatsächlich ein völlig neues Mitglied, das nur für diesen bestimmten Typ vorhanden ist.

Zum Beispiel

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

Die Methode existiert für beide Typen. Wenn Sie Reflection verwenden und die Mitglieder vom Typ Barabrufen, finden Sie tatsächlich zwei Methoden DoSomething(), die genau gleich aussehen. Durch die Verwendung newverstecken Sie die Implementierung in der Basisklasse effektiv, sodass, wenn Klassen von Bar(in meinem Beispiel) dem Methodenaufruf to abgeleitet werden, base.DoSomething()zu Barund nicht Foo.


9

virtual / override teilt dem Compiler mit, dass die beiden Methoden zusammenhängen und dass es unter bestimmten Umständen, wenn Sie glauben, die erste (virtuelle) Methode aufzurufen, tatsächlich richtig ist, stattdessen die zweite (überschriebene) Methode aufzurufen. Dies ist die Grundlage des Polymorphismus.

(new SubClass() as BaseClass).VirtualFoo()

Ruft die überschriebene VirtualFoo () -Methode der Unterklasse auf.

new teilt dem Compiler mit, dass Sie einer abgeleiteten Klasse eine Methode mit demselben Namen wie eine Methode in der Basisklasse hinzufügen, diese jedoch keine Beziehung zueinander haben.

(new SubClass() as BaseClass).NewBar()

Ruft die NewBar () -Methode der BaseClass auf, während:

(new SubClass()).NewBar()

Ruft die NewBar () -Methode der Unterklasse auf.


mag diesen Satz wirklich "sagt dem Compiler"
Mina Gabriel

"Grundlage des Polymorphismus" ist ein weiteres nobles Sprichwort :)
Jeremy Thompson

8

Über die technischen Details hinaus vermittelt die Verwendung von Virtual / Override meiner Meinung nach viele semantische Informationen zum Design. Wenn Sie eine Methode als virtuell deklarieren, geben Sie an, dass Sie erwarten, dass implementierende Klassen möglicherweise ihre eigenen, nicht standardmäßigen Implementierungen bereitstellen möchten. Wenn Sie dies in einer Basisklasse weglassen, wird ebenfalls die Erwartung deklariert, dass die Standardmethode für alle implementierenden Klassen ausreichen sollte. Ebenso kann man abstrakte Deklarationen verwenden, um implementierende Klassen zu zwingen, ihre eigene Implementierung bereitzustellen. Auch hier denke ich, dass dies viel darüber aussagt, wie der Programmierer erwartet, dass der Code verwendet wird. Wenn ich sowohl die Basis- als auch die Implementierungsklasse schreiben würde und neue verwenden würde, würde ich die Entscheidung, die Methode im übergeordneten Element nicht virtuell zu machen, ernsthaft überdenken und meine Absicht ausdrücklich erklären.


Die technischen Erklärungen für new vs override sind zutreffend, aber diese Antwort scheint am hilfreichsten zu sein, um Entwickler bei der Verwendung zu unterstützen.
Sully

4

Der Unterschied zwischen dem Schlüsselwort override und dem neuen Schlüsselwort besteht darin, dass das erstere das Überschreiben von Methoden und das spätere das Ausblenden von Methoden ausführt.

Weitere Informationen finden Sie unter den folgenden Links ...

MSDN und andere


3
  • newSchlüsselwort ist für Verstecken. - bedeutet, dass Sie Ihre Methode zur Laufzeit verstecken. Die Ausgabe basiert auf der Basisklassenmethode.
  • overridezum Überschreiben. - bedeutet, dass Sie Ihre abgeleitete Klassenmethode mit der Referenz der Basisklasse aufrufen. Die Ausgabe basiert auf der abgeleiteten Klassenmethode.

1

Meine Version der Erklärung stammt aus der Verwendung von Eigenschaften , um die Unterschiede zu verstehen.

overrideist einfach genug, oder? Der zugrunde liegende Typ überschreibt den übergeordneten Typ .

newist vielleicht die irreführende (für mich war es). Mit Eigenschaften ist es einfacher zu verstehen:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

Mit einem Debugger können Sie feststellen , dass Foo foohat zwei GetSomething Eigenschaften, wie sie tatsächlich hat zwei Versionen der Immobilie, Foo‚s und Bar‘ s, und zu wissen , die man zu verwenden, c # ‚Picks‘ die Eigenschaft für den aktuellen Typen.

Wenn Sie die Version der Leiste verwenden möchten, hätten Sie Foo foostattdessen überschreiben oder verwenden .

Bar barhat nur 1 , da es für völlig neues Verhalten will GetSomething.


0

Eine Methode nicht mit irgendetwas zu markieren bedeutet: Binden Sie diese Methode mit dem Kompilierungstyp des Objekts, nicht mit dem Laufzeittyp (statische Bindung).

Markieren einer Methode mit virtualMitteln: Binden Sie diese Methode mit dem Laufzeittyp des Objekts und nicht mit dem Kompilierungszeittyp (dynamische Bindung).

Das Markieren einer Basisklassenmethode virtualmit overridein einer abgeleiteten Klasse bedeutet: Dies ist die Methode, die unter Verwendung des Laufzeittyps des Objekts gebunden werden soll (dynamische Bindung).

Das Markieren einer Basisklassenmethode virtualmit newin einer abgeleiteten Klasse bedeutet: Dies ist eine neue Methode, die keine Beziehung zu der Methode mit demselben Namen in der Basisklasse hat und unter Verwendung des Kompilierungszeittyps des Objekts (statische Bindung) gebunden werden sollte.

Das Nichtmarkieren einer Basisklassenmethode virtualin der abgeleiteten Klasse bedeutet: Diese Methode wird als new(statische Bindung) markiert .

Das Markieren einer Methode abstractbedeutet: Diese Methode ist virtuell, aber ich werde keinen Body dafür deklarieren und ihre Klasse ist auch abstrakt (dynamische Bindung).

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.