Warum eine Klasse besiegeln?

96

Ich würde gerne hören, was die Motivation für den Großteil der versiegelten Klassen im .Net-Framework ist. Was ist der Vorteil der Versiegelung einer Klasse? Ich kann mir nicht vorstellen, wie nützlich es sein kann, keine Vererbung zuzulassen, und höchstwahrscheinlich nicht der einzige, der gegen diese Klassen kämpft.

Warum ist das Framework so konzipiert und wäre es nicht eine bahnbrechende Veränderung, alles zu entsiegeln? Es muss einen anderen Grund geben, als nur böse zu sein?

mmiika
quelle

Antworten:

41
  • Manchmal sind Klassen zu kostbar und nicht dazu gedacht, vererbt zu werden.
  • Runtime / Reflection kann bei der Suche nach Typen Vererbungsannahmen für versiegelte Klassen treffen. Ein gutes Beispiel hierfür ist: Es wird empfohlen, Attribute zu versiegeln, um die Laufzeit der Suche zu gewährleisten. type.GetCustomAttributes (typeof (MyAttribute)) wird erheblich schneller ausgeführt, wenn MyAttribute versiegelt ist.

Der MSDN-Artikel zu diesem Thema lautet " Einschränken der Erweiterbarkeit durch Versiegeln von Klassen" .

CVertex
quelle
3
Ich bin froh zu sehen, dass sie jetzt deutlich "mit Vorsicht verwenden" sagen ... ich wünschte, sie würden üben, was sie predigen.
mmiika
13
Das sieht nach einem schlechten Rat für mich aus :(
Jon Skeet
4
@CVertex: Entschuldigung, ich habe nicht versucht, dich zu kritisieren - nur den Artikel.
Jon Skeet
16
@generalt: Ich glaube daran, für Vererbung zu entwerfen oder es zu verbieten. Das Entwerfen für die Vererbung erfordert viel Arbeit und wird die zukünftigen Implementierungen häufig einschränken. Durch die Vererbung werden Anrufer auch unsicher, was genau sie anrufen werden. Es passt auch nicht gut zur Unveränderlichkeit (von der ich ein Fan bin). Ich finde Klassenvererbung nur an relativ wenigen Stellen nützlich (während ich Schnittstellen liebe).
Jon Skeet
1
@CVertex Wenn Sie .NET verwendet haben, sind Sie wahrscheinlich auf das Problem gestoßen und haben es einfach nicht bemerkt. Fast alle .NET-Kernklassen sind versiegelt.
CoryG
103

Klassen sollten entweder für die Vererbung ausgelegt sein oder sie verbieten. Das Entwerfen für die Vererbung ist mit Kosten verbunden:

  • Es kann Ihre Implementierung festlegen (Sie müssen deklarieren, welche Methoden welche anderen Methoden aufrufen, falls ein Benutzer die eine überschreibt, die andere jedoch nicht).
  • Es zeigt Ihre Implementierung und nicht nur die Auswirkungen
  • Es bedeutet, dass Sie beim Entwerfen über mehr Möglichkeiten nachdenken müssen
  • Dinge wie Equals sind in einem Vererbungsbaum schwer zu entwerfen
  • Es erfordert mehr Dokumentation
  • Ein unveränderlicher Typ, der in eine Unterklasse unterteilt ist, kann veränderlich werden (ick).

Punkt 17 von Effective Java geht näher darauf ein - unabhängig davon, dass es im Kontext von Java geschrieben wurde, gilt der Rat auch für .NET.

Persönlich wünschte ich, Klassen wären in .NET standardmäßig versiegelt.

Jon Skeet
quelle
25
Hmm ... wenn Sie eine Klasse erweitern, ist es nicht Ihr Problem, wenn Sie sie brechen?
mmiika
25
Was ist, wenn eine Implementierungsänderung, über die Sie in der Basisklasse keine Kontrolle haben, Sie bricht? Wessen Schuld ist das? Vererbung führt im Grunde genommen zu Fragilität. Die Bevorzugung der Komposition gegenüber der Vererbung fördert die Robustheit, IMO.
Jon Skeet
6
Ja, Schnittstellen sind nett - und ja, Sie können die Komposition trotzdem bevorzugen. Wenn ich jedoch eine nicht versiegelte Basisklasse verfügbar mache, ohne sehr sorgfältig darüber nachzudenken, sollte ich erwarten, dass Änderungen abgeleitete Klassen möglicherweise beschädigen. Das fühlt sich für mich schlecht an. Besser die Klasse versiegeln und Bruch vermeiden, IMO.
Jon Skeet
8
@Joan: Komposition ist eher eine "hat-eine" Beziehung als "ist-eine". Wenn Sie also eine Klasse schreiben möchten, die in gewisser Weise wie eine Liste wirken kann, in anderen jedoch nicht, möchten Sie möglicherweise eine Klasse mit einer Mitgliedsvariablen List <T> erstellen, anstatt sie von List <T> abzuleiten. Sie würden dann verwenden Sie die Liste , um verschiedene Methoden zu implementieren.
Jon Skeet
3
@ThunderGr: Aber das ist mein Punkt: Wenn Sie sich keine Gedanken darüber machen müssen, welche anderen Implementierungen von Unterklassen bereitgestellt werden, können Sie mit Ihrer eigenen Implementierung freier sein. Angenommen, eine Methode wird durch Aufrufen einer anderen Methode implementiert, und beide sind virtuell. Das muss dokumentiert werden - auch wenn es sich wie ein Implementierungsdetail anfühlt . Grundsätzlich werden beim Vererbungsdesign Handschellen hinzugefügt, die in einigen Fällen angemessen sind, in anderen jedoch nicht. Ich hätte lieber eine versiegelte Klasse, die eine Schnittstelle implementiert, als eine nicht versiegelte Klasse mit einer Reihe virtueller Methoden.
Jon Skeet
8

Es scheint, dass sich die offiziellen Microsoft-Richtlinien zum Versiegeln weiterentwickelt haben, seit diese Frage vor ~ 9 Jahren gestellt wurde, und sie von einer Opt-In-Philosophie (standardmäßig versiegeln) zu einem Opt-out (standardmäßig nicht versiegeln) übergegangen sind:

X NICHT Versiegeln Sie Klassen ohne guten Grund.

Das Versiegeln einer Klasse, weil Sie sich kein Erweiterbarkeitsszenario vorstellen können, ist kein guter Grund. Framework-Benutzer erben aus verschiedenen nicht offensichtlichen Gründen gerne von Klassen, z. B. durch Hinzufügen von Convenience-Mitgliedern. Beispiele für nicht offensichtliche Gründe, die Benutzer von einem Typ erben möchten, finden Sie unter Nicht versiegelte Klassen.

Gute Gründe für die Versiegelung einer Klasse sind:

  • Die Klasse ist eine statische Klasse. Siehe Statisches Klassendesign.
  • Die Klasse speichert sicherheitsrelevante Geheimnisse in geerbten geschützten Mitgliedern.
  • Die Klasse erbt viele virtuelle Mitglieder, und die Kosten für die individuelle Versiegelung würden die Vorteile überwiegen, wenn die Klasse nicht versiegelt wird.
  • Die Klasse ist ein Attribut, das eine sehr schnelle Laufzeitsuche erfordert. Versiegelte Attribute weisen etwas höhere Leistungsniveaus auf als nicht versiegelte. Siehe Attribute.

X Deklarieren Sie geschützte oder virtuelle Mitglieder NICHT für versiegelte Typen.

Versiegelte Typen können per Definition nicht vererbt werden. Dies bedeutet, dass geschützte Mitglieder für versiegelte Typen nicht aufgerufen werden können und virtuelle Methoden für versiegelte Typen nicht überschrieben werden können.

✓ Berücksichtigen Sie die von Ihnen überschriebenen Dichtungselemente. Probleme, die sich aus der Einführung virtueller Mitglieder ergeben können (siehe Virtuelle Mitglieder), gelten auch für Überschreibungen, wenn auch in etwas geringerem Maße. Das Versiegeln einer Überschreibung schützt Sie vor diesen Problemen ab diesem Punkt in der Vererbungshierarchie.

Wenn Sie die ASP.Net Core-Codebasis durchsuchen , werden Sie nur etwa 30 Vorkommen finden sealed class, von denen die meisten Attribute und Testklassen sind.

Ich denke, dass die Erhaltung der Unveränderlichkeit ein gutes Argument für die Versiegelung ist.

Ohad Schneider
quelle
4

Ich habe diesen Satz in der msdn-Dokumentation gefunden: "Versiegelte Klassen werden hauptsächlich verwendet, um eine Ableitung zu verhindern. Da sie niemals als Basisklasse verwendet werden können, können einige Laufzeitoptimierungen das Aufrufen versiegelter Klassenmitglieder etwas beschleunigen."

Ich weiß nicht, ob die Leistung der einzige Vorteil von versiegelten Klassen ist und ich persönlich würde auch gerne andere Gründe wissen ...

bruno conde
quelle
4
Es wäre interessant zu sehen, über welche Art von Leistungsvorteil sie sprechen ...
mmiika
3

Die Leistung ist ein wichtiger Faktor, zum Beispiel ist die String-Klasse in Java endgültig (<- versiegelt) und der Grund dafür ist nur die Leistung. Ich denke, ein weiterer wichtiger Punkt ist die Vermeidung des hier im Detail beschriebenen Problems der spröden Basisklasse: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Wenn Sie ein Framework bereitstellen, ist es wichtig, dass ältere Projekte gewartet werden können und dass Ihr Framework aktualisiert wird, um das Problem der spröden Basisklasse zu vermeiden

Peter Parker
quelle
Der Grund dafür, dass String in Java endgültig ist, ist nicht die Leistung, sondern die Sicherheit.
CesarB
@CesarB: Ja, aber auch String ist keine normale Java-Klasse. Es ist die einzige (meiner Meinung nach) Klasse in Java, die das Überladen von Operatoren unterstützt (weitere Informationen finden Sie hier im Abschnitt "Sogar C und Java haben (fest codierte) Überladen von Operatoren"), was in einer normalen Klasse nicht möglich ist. Aus diesem Grund kann die StringKlasse möglicherweise nicht einmal in eine Unterklasse eingeteilt werden, selbst wenn sie nicht endgültig wäre.
Wchargin
1

Versiegelt wird verwendet, um das "Problem der spröden Basisklasse" zu verhindern. Ich habe in MSDN einen guten Artikel gefunden , der das erklärt.

ihebiheb
quelle
0

Durch das Versiegeln können Sie einige geringfügige Leistungssteigerungen erzielen. Dies trifft in der Welt der JITs und der faulen Pessimisierung weniger zu als in der Welt von beispielsweise C ++. Da .NET jedoch nicht so gut wie die Pessimisierung ist, wie Java-Compiler hauptsächlich aufgrund unterschiedlicher Designphilosophien, ist es dennoch nützlich. Es teilt dem Compiler mit, dass er alle virtuellen Methoden direkt aufrufen kann, anstatt sie indirekt über die vtable aufzurufen.

Es ist auch wichtig, wenn Sie eine „geschlossene Welt“ für Dinge wie den Gleichstellungsvergleich wollen. Normalerweise bin ich, sobald ich eine virtuelle Methode definiert habe, ziemlich fertig damit, einen Begriff des Gleichheitsvergleichs zu definieren, der die Idee wirklich umsetzt. Andererseits kann ich es möglicherweise mit der virtuellen Methode für eine bestimmte Unterklasse der Klasse definieren. Das Versiegeln dieser Klasse stellt sicher, dass Gleichheit wirklich gilt.

Edward KMETT
quelle
0

Das Versiegeln einer Klasse erleichtert die Verwaltung verfügbarer Ressourcen.

Jeff Dunlop
quelle
0

Um zu bestimmen, ob eine Klasse, Methode oder Eigenschaft versiegelt werden soll, sollten Sie im Allgemeinen die folgenden zwei Punkte berücksichtigen:

• Die potenziellen Vorteile, die das Ableiten von Klassen durch die Möglichkeit zur Anpassung Ihrer Klasse erhalten kann.

• Das Potenzial, dass das Ableiten von Klassen Ihre Klassen so ändern kann, dass sie nicht mehr richtig oder wie erwartet funktionieren.

user3629577
quelle
0

Eine weitere Überlegung ist, dass versiegelte Klassen in Ihren Unit-Tests nicht gestoppt werden können. Aus der Microsoft-Dokumentation :

Versiegelte Klassen oder statische Methoden können nicht gestoppt werden, da Stub-Typen vom Versand virtueller Methoden abhängen. Verwenden Sie in solchen Fällen Shim-Typen wie unter Verwenden von Shims beschrieben, um Ihre Anwendung für Unit-Tests von anderen Baugruppen zu isolieren

Dave Clark
quelle