2010-10-01 19 views
44

Ich bin ein bisschen verwirrt über das Überschreiben vs. Verstecken einer Methode in C#. Praktische Anwendungen von jedem würde auch geschätzt werden, sowie eine Erklärung für , wenn man jeweils verwenden würde.Override vs Methode versteckt

Ich bin verwirrt über das Überschreiben - warum überschreiben wir? Was ich bisher gelernt habe, ist, dass wir durch Übersteuern einer Methode einer abgeleiteten Klasse die gewünschte Implementierung bereitstellen können, ohne die Signatur zu ändern.

Wenn ich die Methode der Superklasse nicht überschreibe und Änderungen an der Methode in der Unterklasse vornimmt, werden dann Änderungen an der Superklassenmethode vorgenommen?

Ich bin auch verwirrt über die folgenden - was zeigt dies?

class A 
{ 
    virtual m1() 
    { 
     console.writeline("Bye to all"); 
    } 
} 

class B : A 
{ 
    override m1() 
    { 
     console.writeLine("Hi to all"); 
    } 
} 

class C 
{ 
    A a = new A(); 
    B b = new B(); 
    a = b; (what is this) 
    a.m1(); // what this will print and why? 

    b = a; // what happens here? 
} 
+0

nach Googeln bin ich hier mit meinen Problemen gewesen ... – NoviceToDotNet

+0

Sieht so aus, als müssten Sie grundlegende C# -Programmierungskonzepte lernen, bevor Sie das Überschreiben und Verbergen von Methoden verstehen können. Probieren Sie eines der vielen C# Anfängerbücher aus. – thecoop

+0

1) a.m1(); // was wird gedruckt und warum? Einfach Es wird "hai an alle" drucken, weil Sie das Method Overriding-Konzept verwenden. 2) b = a; // was geschieht hier? Hier erhalten wir einen Kompilierungsfehler, da das Kindklassenobjekt nicht auf das Basisklassenobjekt verweist. Umgekehrt ist das Vererbungskonzept möglich. – Krish

Antwort

101

Bedenken Sie:

public class BaseClass 
{ 
    public void WriteNum() 
    { 
    Console.WriteLine(12); 
    } 
    public virtual void WriteStr() 
    { 
    Console.WriteLine("abc"); 
    } 
} 

public class DerivedClass : BaseClass 
{ 
    public new void WriteNum() 
    { 
    Console.WriteLine(42); 
    } 
    public override void WriteStr() 
    { 
    Console.WriteLine("xyz"); 
    } 
} 
/* ... */ 
BaseClass isReallyBase = new BaseClass(); 
BaseClass isReallyDerived = new DerivedClass(); 
DerivedClass isClearlyDerived = new DerivedClass(); 

isReallyBase.WriteNum(); // writes 12 
isReallyBase.WriteStr(); // writes abc 
isReallyDerived.WriteNum(); // writes 12 
isReallyDerived.WriteStr(); // writes xyz 
isClearlyDerived.WriteNum(); // writes 42 
isClearlyDerived.writeStr(); // writes xyz 

Zwingende der klassische OO Weg ist, in dem eine abgeleitete Klasse spezifischere Verhalten als eine Basisklasse haben kann (in einigen Sprachen haben Sie keine andere Wahl, dies zu tun). Wenn eine virtuelle Methode für ein Objekt aufgerufen wird, wird die am weitesten abgeleitete Version der Methode aufgerufen. Daher wird, obwohl es sich um isReallyDerived als BaseClass handelt, die in DerivedClass definierte Funktionalität verwendet.

Verstecken bedeutet, dass wir eine völlig andere Methode haben. Wenn wir WriteNum() auf isReallyDerived anrufen, dann gibt es keine Möglichkeit zu wissen, dass es einen anderen WriteNum() auf DerivedClass gibt, so dass es nicht aufgerufen wird. Sie kann nur aufgerufen werden, wenn es sich um das Objekt als a DerivedClass handelt.

Die meiste Zeit ist Verbergen schlecht. Im Allgemeinen sollten Sie entweder eine Methode als virtuell haben, wenn sie wahrscheinlich in einer abgeleiteten Klasse geändert wird, und sie in der abgeleiteten Klasse überschreiben. Es gibt jedoch zwei Dinge, für die es nützlich ist:

  1. Vorwärtskompatibilität. Wenn eine DoStuff() Methode hatte, und dann später BaseClass wurde geändert, um eine DoStuff() Methode hinzuzufügen (denken Sie daran, dass sie von verschiedenen Leuten geschrieben werden und in verschiedenen Baugruppen existieren), dann hätte ein Verbot auf Mitgliederverstecken DerivedClass Buggy ohne es plötzlich gemacht Ändern. Auch wenn das neue DoStuff() auf BaseClass virtuell war, dann könnte das automatisch auf DerivedClass eine Überschreibung von es könnte dazu führen, dass die bereits bestehende Methode aufgerufen wird, wenn es nicht sollte. Daher ist es gut, dass das Verbergen der Standard ist (wir verwenden new, um klarzustellen, dass wir uns definitiv verstecken wollen, aber es ausblendet und eine Warnung bei der Kompilierung ausgibt).

  2. Poor-Mann-Kovarianz. Betrachten Sie eine Clone() Methode auf BaseClass, die eine neue BaseClass zurückgibt, die eine Kopie von dem ist, das erstellt wird. In der Überschreibung auf DerivedClass wird dies eine DerivedClass erstellen, aber es als BaseClass zurückgeben, die nicht so nützlich ist. Was wir tun könnten, ist eine virtuelle geschützte CreateClone(), die überschrieben wird.In BaseClass haben wir eine Clone(), die das Ergebnis davon zurückgibt - und alles ist gut - in DerivedClass verstecken wir dies mit einer neuen Clone(), die eine DerivedClass zurückgibt. Beim Aufruf von Clone() unter BaseClass wird immer eine BaseClass Referenz zurückgegeben, die je nach Bedarf ein BaseClass Wert oder ein DerivedClass Wert ist. Wenn Clone() auf DerivedClass aufgerufen wird, wird ein DerivedClass Wert zurückgegeben, was wir in diesem Kontext wünschen. Es gibt andere Varianten dieses Prinzips, aber es sollte angemerkt werden, dass sie alle ziemlich selten sind.

Eine wichtige Sache mit dem zweiten Fall zu beachten ist, dass wir gerade verwendet haben, um entfernen Überraschungen auf die Telefonvorwahl, wie die Person versteckt DerivedClass könnte mit einigermaßen seine Clone() erwarten ein DerivedClass zurückzukehren. Die Ergebnisse von irgendwelchen Arten, wie es genannt werden könnte, werden miteinander konsistent gehalten. Die meisten Fälle von Verstecken riskieren Überraschungen, weshalb sie im Allgemeinen verpönt sind. Diese ist gerade deshalb gerechtfertigt, weil sie genau das Problem löst, das oft durch Verstecken verursacht wird.

In allem ist Verstecken manchmal notwendig, selten nützlich, aber im Allgemeinen schlecht, also seien Sie sehr vorsichtig.

+0

sir Was, wenn ich public virtual void WriteNum() und in sub claass ..... ich benutze öffentliche new virual void WriteNum() welche Methode wird aufgerufen isReallyBase.WriteNum(); und y – NoviceToDotNet

+0

@NoviceToDotNet mit 'new virtual' bedeutet, dass es die abgeleitete Version verbirgt, aber selbst virtuell sein kann.Wenn Sie 'WriteNum()' entweder auf 'isReallyBase' oder' isReallyDerived' aufrufen, dann würde die in 'BaseClass' definierte Version aufgerufen werden, da sie nicht überschrieben wird, aber wenn Sie sie' isClearlyDerived' aufgerufen haben, dann wird die versteckte Version heißen. Außerdem könnten Sie eine Überschreibung in einer weiteren Unterklasse haben (zB "DerivedFromDerivedClass") und diese Überschreibung würde aufgerufen, wenn "DerivedFromDerivedClass" über eine 'DerivedFromDerivedClass' Variable oder eine' DerivedClass' Variable ... –

+0

und auch in Falls wir die Methode der Superklasse nicht überschreiben, ist es nicht zwingend, was es tut, ist ReallyDerived.WriteStr(); – NoviceToDotNet

23

Zwingende ist, wenn Sie eine neue override Implementierung einer Methode in einer abgeleiteten Klasse zur Verfügung stellen, wenn diese Methode in der Basisklasse als virtual definiert ist.

Verstecke sind, wenn Sie eine neue Implementierung eines Verfahrens in einer abgeleiteten Klasse zur Verfügung stellen, wenn diese Methode nicht in der Basisklasse als virtual, oder wenn Ihre neue Implementierung ist nicht festgelegt override definiert ist.

Verstecken ist sehr oft schlecht; Sie sollten generell versuchen, es nicht zu tun, wenn Sie es überhaupt vermeiden können. Verstecken kann dazu führen, dass unerwartete Dinge passieren, weil verdeckte Methoden nur verwendet werden, wenn sie für eine Variable des von Ihnen definierten Typs aufgerufen werden, nicht wenn eine Basisklassenreferenz verwendet wird. Andererseits werden virtuelle Methoden, die überschrieben werden, enden Die richtige Methodenversion wird aufgerufen, auch wenn sie mit der Basisklassenreferenz einer untergeordneten Klasse aufgerufen wird.

Betrachten wir zum Beispiel diese Klassen:

public class BaseClass 
{ 
    public virtual void Method1() //Virtual method 
    { 
    Console.WriteLine("Running BaseClass Method1"); 
    } 
    public void Method2() //Not a virtual method 
    { 
    Console.WriteLine("Running BaseClass Method2"); 
    } 
} 
public class InheritedClass : BaseClass 
{ 
    public override void Method1() //Overriding the base virtual method. 
    { 
    Console.WriteLine("Running InheritedClass Method1"); 
    } 
    public new void Method2() //Can't override the base method; must 'new' it. 
    { 
    Console.WriteLine("Running InheritedClass Method2"); 
    } 
} 

ist es so Nennen wir, mit einer Instanz von InheritedClass, in einem passenden Referenz:

InheritedClass inherited = new InheritedClass(); 
inherited.Method1(); 
inherited.Method2(); 

Das gibt, was Sie erwarten sollten; Beide Methoden geben an, dass sie die InheritedClass-Versionen ausführen.

Lauf InheritedClass Method1
Lauf InheritedClass Method2

Dieser Code eine Instanz der gleichen, InheritedClass schafft, sondern speichert sie in einem Baseclass-Referenz:

BaseClass baseRef = new InheritedClass(); 
baseRef.Method1(); 
baseRef.Method2(); 

Normalerweise unter OOP Prinzipien, sollten Sie die gleiche Ausgabe wie im obigen Beispiel erwarten. Aber Sie müssen nicht die gleiche Leistung erhalten:

Lauf InheritedClass Method1
Laufbaseclass Method2

Wenn Sie den Code InheritedClass schrieb, haben Sie alle Anrufe an Method2() wollte den Code, den Sie laufen schrieb darin. Normalerweise würde dies so funktionieren - vorausgesetzt, Sie arbeiten mit einer virtual Methode, die Sie überschrieben haben.Da Sie jedoch eine new/hidden-Methode verwenden, ruft sie stattdessen die Version der von Ihnen verwendeten Referenz auf.


Wenn das das Verhalten Sie wirklich wollen, dann; da gehst du. Ich würde jedoch dringend empfehlen, dass, wenn Sie das möchten, ein größeres architektonisches Problem mit dem Code auftreten könnte.

+0

Ich würde nicht sagen, dass Sie _never_ eine Methode verbergen sollten. Manchmal finde ich das Verbergen sehr nützlich (z. B. den Rückgabewert in eine Unterklasse zu ändern) – thecoop

+0

Deshalb habe ich gesagt, dass du es nie wieder tun sollst :) –

+0

Ja, aber für den Fall, dass @thecoop erwähnt, dass du sehr bewusst willst Versuche zu verstecken. Die Hauptsache, denke ich, ist, dass normales Verstecken böse Überraschungen bringen kann, aber wenn man es für die Kovarianz benutzt, entfernt man eigentlich Überraschungen. –

2

Das Überschreiben von Methoden überschreibt einfach eine Standardimplementierung einer Basisklassenmethode in der abgeleiteten Klasse.

Methode Ausblenden: Sie können in einer abgeleiteten Klasse

als

class Foo 
{ 
    public virtual void foo1() 
    { 

    } 
} 

class Bar:Foo 
{ 
    public new virtual void foo1() 
    { 

    } 
} 

jetzt den Einsatz von ‚neuen‘ Schlüsselwort vor einer virtuellen Methode, wenn Sie eine andere Klasse Bar1 machen, die von Bar abgeleitet ist, können Sie kann foo1 überschreiben, was in Bar definiert ist.

+0

y kann virtual mit override Schlüsselwort verwenden, wenn in Ihrem Beispiel ich .... Klasse myclass: bar {dann wird es die Foo-Klasse foo1() oder bar-Klasse foo1(), um es außer Kraft} auch wenn in der Bar-Klasse Anstatt das foo1() zu überschreiben, überschreibe ich es und setze es virtuell vor es dann wird es an myClass vererbt und wieder durch Überschreiben kann ich die Methode der Bar Klasse foo1() zu myClass – NoviceToDotNet

+0

@NoviceToDotNet Nein, kann virtuell verwendet werden in einer von Bar abgeleiteten Klasse, überschreibt jedoch Bar.foo1 und nicht Foo.foo1. Die obige Mischung aus Verbergen und Virtuellem ist gültig, obwohl sie in der Kombination der beiden vielleicht nicht die beste Erklärung für den Unterschied zwischen den beiden ist. –