Nur um sicherzustellen, dass wir uns auf derselben Seite befinden, bedeutet die Methode "Verstecken", dass eine abgeleitete Klasse ein Element mit demselben Namen wie eines in der Basisklasse definiert (das, wenn es sich um eine Methode / Eigenschaft handelt, nicht als virtuell / überschreibbar markiert ist ) und beim Aufrufen von einer Instanz der abgeleiteten Klasse im "abgeleiteten Kontext" wird das abgeleitete Element verwendet, während beim Aufrufen von derselben Instanz im Kontext ihrer Basisklasse das Basisklassenelement verwendet wird. Dies unterscheidet sich von der Elementabstraktion / -überschreibung, bei der das Basisklassenelement erwartet, dass die abgeleitete Klasse eine Ersetzung definiert, und von Bereichs- / Sichtbarkeitsmodifikatoren, die ein Element vor Verbrauchern "verbergen", die außerhalb des gewünschten Bereichs liegen.
Die kurze Antwort auf die Frage, warum dies zulässig ist, lautet, dass dies die Entwickler zwingen würde, gegen mehrere wichtige Grundsätze des objektorientierten Designs zu verstoßen.
Hier ist die längere Antwort; Betrachten Sie zunächst die folgende Klassenstruktur in einem alternativen Universum, in dem C # das Ausblenden von Mitgliedern nicht zulässt:
public interface IFoo
{
string MyFooString {get;}
int FooMethod();
}
public class Foo:IFoo
{
public string MyFooString {get{return "Foo";}}
public int FooMethod() {//incredibly useful code here};
}
public class Bar:Foo
{
//public new string MyFooString {get{return "Bar";}}
}
Wir möchten dem Mitglied in Bar das Kommentarzeichen entziehen und auf diese Weise erlauben, dass Bar einen anderen MyFooString bereitstellt. Dies können wir jedoch nicht tun, da dies gegen das Verbot der alternativen Realität verstoßen würde, Mitglieder zu verstecken. Dieses Beispiel ist voller Bugs und ein gutes Beispiel dafür, warum Sie es möglicherweise verbieten möchten. Welche Konsolenausgabe würden Sie beispielsweise erhalten, wenn Sie Folgendes tun würden?
Bar myBar = new Bar();
Foo myFoo = myBar;
IFoo myIFoo = myFoo;
Console.WriteLine(myFoo.MyFooString);
Console.WriteLine(myBar.MyFooString);
Console.WriteLine(myIFoo.MyFooString);
Ich bin mir nicht sicher, ob in der letzten Zeile "Foo" oder "Bar" steht. Sie erhalten definitiv "Foo" für die erste Zeile und "Bar" für die zweite, obwohl alle drei Variablen genau dieselbe Instanz mit genau demselben Status referenzieren.
Daher entmutigen die Designer der Sprache in unserem alternativen Universum diesen offensichtlich schlechten Code, indem sie das Verbergen von Eigenschaften verhindern. Jetzt müssen Sie als Programmierer genau das tun. Wie kommst du um die Begrenzung herum? Eine Möglichkeit besteht darin, die Eigenschaft von Bar anders zu benennen:
public class Bar:Foo
{
public string MyBarString {get{return "Bar";}}
}
Völlig legal, aber es ist nicht das Verhalten, das wir wollen. Eine Instanz von Bar wird immer "Foo" für die Eigenschaft MyFooString erzeugen, wenn wir wollten, dass es "Bar" erzeugt. Wir müssen nicht nur wissen, dass unser IFoo speziell eine Bar ist, sondern wir müssen auch wissen, wie man die verschiedenen Accessors verwendet.
Wir könnten auch plausibel die Eltern-Kind-Beziehung vergessen und die Schnittstelle direkt implementieren:
public class Bar:IFoo
{
public string MyFooString {get{return "Bar";}}
public int FooMethod() {...}
}
Für dieses einfache Beispiel ist es eine perfekte Antwort, solange Sie sich nur darum kümmern, dass Foo und Bar beide IFoos sind. Der Verwendungscode, der in einigen Beispielen angegeben ist, kann nicht kompiliert werden, da ein Balken kein Foo ist und nicht als solcher zugewiesen werden kann. Wenn Foo jedoch eine nützliche Methode "FooMethod" hatte, die Bar benötigte, können Sie diese Methode jetzt nicht erben. Sie müssten entweder den Code in Bar klonen oder kreativ werden:
public class Bar:IFoo
{
public string MyFooString {get{return "Bar";}}
private readonly theFoo = new Foo();
public int FooMethod(){return theFoo.FooMethod();}
}
Dies ist ein offensichtlicher Hack, und obwohl einige Implementierungen von OO-Sprachspezifikationen kaum mehr als dies bedeuten, ist dies konzeptionell falsch. wenn die Verbraucher von Notwendigkeit Bar Foo Funktionalität aussetzen, sollte Bar sein ein Foo, nicht haben eine Foo.
Wenn wir Foo kontrollieren, können wir es natürlich virtuell machen und es dann außer Kraft setzen. Dies ist die konzeptionelle Best Practice in unserem aktuellen Universum, wenn erwartet wird, dass ein Mitglied außer Kraft gesetzt wird, und sie in jedem alternativen Universum gilt, in dem das Ausblenden nicht zulässig ist:
public class Foo:IFoo
{
public virtual string MyFooString {get{return "Foo";}}
//...
}
public class Bar:Foo
{
public override string MyFooString {get{return "Bar";}}
}
Das Problem dabei ist, dass der Zugriff auf virtuelle Mitglieder unter der Haube relativ teuer ist und Sie ihn normalerweise nur dann ausführen möchten, wenn Sie ihn benötigen. Das Fehlen des Versteckens zwingt Sie jedoch dazu, den Mitgliedern gegenüber pessimistisch zu sein, die ein anderer Codierer, der Ihren Quellcode nicht kontrolliert, möglicherweise neu implementieren möchte. Die "beste Praxis" für eine nicht versiegelte Klasse wäre, alles virtuell zu machen, es sei denn, Sie wollten dies ausdrücklich nicht. Es gibt Ihnen auch immer noch nicht das genaue Verhalten, sich zu verstecken. Die Zeichenfolge ist immer "Bar", wenn die Instanz eine Bar ist. Manchmal ist es wirklich nützlich, die Ebenen der verborgenen Zustandsdaten basierend auf der Ebene der Vererbung, auf der Sie arbeiten, zu nutzen.
Zusammenfassend ist das Ermöglichen des Versteckens von Mitgliedern das geringere dieser Übel. Wenn dies nicht der Fall ist, würde dies im Allgemeinen zu schlimmeren Gräueltaten führen, die gegen objektorientierte Prinzipien begangen werden, als wenn dies zulässig wäre.