Was sind einige praktische Verwendungen des Modifikators "new" in C # in Bezug auf das Ausblenden?

21

Ein Mitarbeiter und ich haben das Verhalten des newSchlüsselworts in C # untersucht, das für das Konzept des Versteckens gilt. Aus der Dokumentation :

Verwenden Sie den neuen Modifikator, um ein von einer Basisklasse geerbtes Element explizit auszublenden. Um ein geerbtes Element auszublenden, deklarieren Sie es in der abgeleiteten Klasse mit demselben Namen und ändern Sie es mit dem neuen Modifikator.

Wir haben die Dokumentation gelesen und verstehen, was es im Grunde tut und wie es es tut. Was wir nicht wirklich in den Griff bekommen konnten, ist, warum Sie es an erster Stelle tun müssten. Der Modifikator ist seit 2003 da und wir arbeiten beide schon länger mit .Net zusammen und es ist nie aufgetaucht.

Wann wäre dieses Verhalten in praktischer Hinsicht notwendig (z. B. auf einen Geschäftsfall bezogen)? Ist dies eine Funktion, die ihre Nützlichkeit überlebt hat, oder ist das, was sie macht, einfach ungewöhnlich genug in dem, was wir tun (insbesondere machen wir Webformulare und MVC-Anwendungen und einige WinForms und WPF mit kleinen Faktoren)? Wenn wir dieses Schlüsselwort ausprobierten und damit spielten, fanden wir einige Verhaltensweisen, die ein wenig gefährlich erscheinen, wenn sie missbraucht werden.

Das klingt ein wenig offen, aber wir suchen nach einem bestimmten Anwendungsfall, der auf eine Geschäftsanwendung angewendet werden kann, für die dieses spezielle Tool nützlich ist.

Joel Etherton
quelle
9
Vielleicht haben Sie es auch gelesen, aber es gibt einen interessanten Artikel von Eric Lippert (C # -Compiler-Entwickler) darüber, warum das Ausblenden von Methoden zu C # hinzugefügt wurde: blogs.msdn.com/b/ericlippert/archive/2008/05/21/… . Das beantwortet einen Teil Ihrer Frage, aber ich habe keinen Business Case für Sie parat, also schreibe ich das in einen Kommentar.
Jalayn
3
@ Jalayn: Der Geschäftsfall wird von Eric in diesem Beitrag erörtert
Brian

Antworten:

22

Sie können es verwenden, um die Kovarianz vom Rückgabetyp nachzuahmen. Eric Lipperts Erklärung . Eric liefert diesen Beispielcode:

abstract class Enclosure
{
    protected abstract Animal GetContents();
    public Animal Contents() { return this.GetContents(); }
}
class Aquarium : Enclosure
{
    public new Fish Contents() { ... }
    protected override Animal GetContents() { return this.Contents(); }
}

Dies ist eine Problemumgehung. public override Fish Contents() { ... }ist nicht legal, obwohl sie sicher ist.

Im Allgemeinen sollten Sie das Ausblenden von Methoden nicht verwenden, da dies für die Verbraucher Ihrer Klasse verwirrend ist (das obige spezifische Beispiel weist dieses Problem nicht auf). Nennen Sie Ihre neue Methode einfach etwas anderes, wenn Sie eine vorhandene Methode nicht überschreiben möchten.

In der Praxis ist es wahrscheinlich, dass Sie Methoden ausblenden müssen, wenn der Anbieter einer Basisklasse eine generische Methode hinzugefügt hat, die Sie bereits zu einer abgeleiteten Klasse hinzugefügt haben. Solch ein Programm kompiliert (und gibt Warnungen aus), ohne das neue Schlüsselwort, aber das Hinzufügen newsagt: "Ich weiß, dass meine Version dieser Methode die Version der Basisklasse ersetzt. Das ist schrecklich und verwirrend, aber wir bleiben dabei." Das ist immer noch besser, als die abgeleitete Klasse zu zwingen, ihre Methode umzubenennen.

Nur zuzulassen, dass die abgeleitete Methode als Überschreibung behandelt wird, würde Probleme verursachen. Ohne Rücksicht auf die Implementierung des Compilers unterscheidet sich die neue Methode semantisch von der Basismethode, aber aufgrund des Polymorphismus wird die neue Methode aufgerufen, wenn Sie aufgefordert werden, eine Methode mit demselben Namen aufzurufen.

Diese Situation wird in diesem Beitrag von Eric Lippert ausführlich erörtert .

Brian
quelle
1
+1, weil newes ein Marker für "das ist schrecklich und verwirrend" ist.
Avner Shahar-Kashtan
Ich denke, Erics Beispiel ist sehr hilfreich, wenn auch nur, weil ich in einer ähnlichen Situation bin ... Ich habe eine Basisklasse, die eine nicht-triviale Methode implementiert, die TBase zurückgibt, und eine abgeleitete Klasse, die TDerived zurückgeben sollte. In diesem Fall fühlt es sich wie ein notwendiges Übel an.
Kyle Baran
4

Ich denke, es ist da, falls Sie es brauchen, um etwas zu tun, woran die Sprachdesigner vielleicht nicht gedacht haben. C # war in vielerlei Hinsicht eine Reaktion auf frühe Versionen von Java. Und eine Sache, die Java getan hat, war, sehr explizit Entwickler in die Schublade zu stecken, um die Möglichkeit auszuschließen, dass Entwickler sich selbst in die Füße schießen. C # verfolgte einen etwas anderen Ansatz und gab den Entwicklern ein wenig mehr Kraft, um den Entwicklern mehr Möglichkeiten zu geben, sich selbst in die Füße zu schießen. Ein Beispiel ist das unsafeSchlüsselwort. Dieses newSchlüsselwort ist ein anderes.

Nun, es ist wahrscheinlich nicht so nützlich wie, unsafeaber wenn Sie erst einmal in eine Sprachspezifikation geraten sind, ist es schwierig, aus einer Sprachspezifikation herauszukommen.

Wyatt Barnett
quelle
5
Java hat keine neuen, da Java alle Methoden als virtuell behandelt. Laut Eric Lippert besteht die Motivation für die Unterstützung von Neuem darin, das Problem der spröden Basisklasse zu lösen. Das Vorhandensein von Neuem ist für die Verwendung in der Praxis erforderlich, nicht nur für die Verwendung in Bibliotheken auf niedriger Ebene. Java, das keine neuen (und keine nicht virtuellen) Methoden hat, bedeutet, dass, wenn eine Basisklasse eine neue Methode einführt, die bereits in abgeleiteten Klassen verwendet wird, vorhandener Code möglicherweise beschädigt wird, obwohl der Entwickler der Basisklasse zugelassen werden sollte Code, der ihn verbraucht, nicht zu bemerken.
Brian
3

Es sagt dem Leser, dass "ich die Implementierung dieser Methode durch die Basisklasse absichtlich versteckt habe" und nicht versehentlich.

Vegio
quelle
5
Das scheint mir die Frage zu sein. Das OP weiß, was es tut, will aber wissen, warum.
Brian
0

Möglicherweise möchten Sie das vorherige Mitglied über einen anderen Namen verfügbar machen:

class VehicleClass
{
  public int AnyProperty
  {
    get; set;
  }

  public int AnyFunction() { return 0; }
} // VehicleClass

class IntermediateClass : VehicleClass
{
  public int PreviousAnyProperty
  {
    get { return AnyProperty; }
    set { AnyProperty = value  }
  }

  public int PreviousAnyFunction() { return AnyFunction(); }
} // IntermediateClass 

class CarClass : IntermediateClass
{
  public new int AnyProperty
  {
    get ; set ;
  }

  public new int AnyFunction() { return 5; }
} // class CarClass

class ExampleClass
{

  public static void Main()
  {
    using (CarClass MyCar = new CarClass())
    {
      int AnyInt1 = MyCar.PreviousAnyProperty;
      MyCar.PreviousAnyProperty = 7;

      int AnyInt2 = MyCar.PreviousAnyFunction();

      int AnyInt3 = MyCar.AnyProperty;
      MyCar.AnyProperty = 45;

      int AnyInt4 = MyCar.AnyFunction();
    }
  } // static void Main()

} // class CarClass

Prost.

umlcat
quelle
Ich bin mit der Funktionsweise vertraut, aber meine Frage ist wirklich, warum Sie das vorherige Mitglied unter einem anderen Namen verfügbar haben möchten. Dies bricht alle Arten von Verträgen, macht bestimmte generische Stücke unbrauchbar.
Joel Etherton
@Joel Etherton: Wie Sie vielleicht bereits wissen, müssen Entwickler manchmal den Programmcode anderer Leute "auswählen". Und wir sind möglicherweise nicht berechtigt, Klassen zu ändern oder zu erweitern. Möglicherweise müssen sowohl das vorherige als auch das neue Mitglied verwendet werden.
umlcat
0

Ich fand es sehr nützlich, wenn ich eine Testsuite für Legacy-Code erstellte. Es ermöglicht mir, externe Abhängigkeiten auszublenden, z. B. das Abfragen einer Datenbank innerhalb einer Methode, die von anderen Methoden verwendet wird. Um die anderen Methoden testen zu können, kann die ursprüngliche Logik, die von externen Ressourcen abhängt, ausgeblendet werden, ohne dass diesen Methoden in Testklassen das virtuelle Schlüsselwort hinzugefügt werden muss.

Spacy
quelle