Perché sigillare una classe?


96

Mi piacerebbe sapere qual è la motivazione dietro la maggior parte delle classi sigillate nel framework .Net. Qual è il vantaggio di suggellare una classe? Non riesco a capire come non consentire l'eredità possa essere utile e molto probabilmente non l'unico a combattere queste classi.

Allora, perché il framework è stato progettato in questo modo e non sarebbe un cambiamento radicale aprire tutto? Ci deve essere un'altra ragione, ma solo essere malvagi?



Risposte:


41
  • A volte le classi sono troppo preziose e non progettate per essere ereditate.
  • Runtime / Reflection può fare ipotesi di ereditarietà sulle classi sigillate quando si cercano i tipi. Un ottimo esempio di ciò è: si consiglia di sigillare gli attributi per la velocità di runtime della ricerca. type.GetCustomAttributes (typeof (MyAttribute)) funzionerà molto più velocemente se MyAttribute è bloccato.

L'articolo MSDN per questo argomento è Limitare l'estensibilità per classi di sigillatura .


3
Sono contento di vedere che ora dicono chiaramente "usa con cautela" ... vorrei che praticassero ciò che predicano.
mmiika

13
Mi sembra un cattivo consiglio :(
Jon Skeet

4
@CVertex: Scusa, non stavo cercando di criticarti, solo l'articolo.
Jon Skeet

16
@generalt: credo nella progettazione per l'eredità o nel proibirla. Progettare per l'ereditarietà richiede un bel po 'di lavoro e spesso limiterà le implementazioni in futuro. L'ereditarietà introduce anche l'incertezza nei chiamanti su esattamente ciò a cui chiameranno. Inoltre non si combina bene con l'immutabilità (di cui sono un fan). Trovo utile solo l'ereditarietà delle classi in un numero relativamente piccolo di posti (mentre adoro le interfacce).
Jon Skeet

1
@CVertex Se hai usato .NET probabilmente hai riscontrato il problema e semplicemente non l'hai notato, quasi tutte le classi core .NET sono sigillate.
CoryG

103

Le classi dovrebbero essere progettate per l'eredità o proibirla. C'è un costo per la progettazione per l'ereditarietà:

  • Può definire la tua implementazione (devi dichiarare quali metodi chiameranno quali altri metodi, nel caso in cui un utente sovrascriva uno ma non l'altro)
  • Rivela la tua implementazione piuttosto che solo gli effetti
  • Significa che devi pensare a più possibilità durante la progettazione
  • Cose come Equals sono difficili da progettare in un albero di ereditarietà
  • Richiede più documentazione
  • Un tipo immutabile che è sottoclasse può diventare mutabile (ick)

L'articolo 17 di Effective Java entra in maggiori dettagli su questo - indipendentemente dal fatto che sia scritto nel contesto di Java, il consiglio si applica anche a .NET.

Personalmente vorrei che le classi fossero sigillate per impostazione predefinita in .NET.


26
Hmm .. se estendi una classe, non è un tuo problema se la interrompi?
mmiika

25
Cosa succede se una modifica all'implementazione su cui non hai controllo nella classe base ti rompe? Di chi è la colpa? L'ereditarietà introduce la fragilità, fondamentalmente. Favorire la composizione rispetto all'ereditarietà promuove la robustezza, IMO.
Jon Skeet

6
Sì, le interfacce sono belle e sì, puoi comunque favorire la composizione. Ma se espongo una classe base non sigillata senza pensarci molto attentamente, dovrei aspettarmi che le modifiche potrebbero rompere le classi derivate. Mi sembra una brutta cosa. Meglio sigillare la classe ed evitare rotture, IMO.
Jon Skeet

8
@ Joan: la composizione è una relazione "ha-un" piuttosto che "è-un". Quindi, se vuoi scrivere una classe che può agire come un elenco in alcuni modi, ma non in altri, potresti voler creare una classe con una variabile membro List <T>, piuttosto che derivare da List <T>. Dovresti quindi utilizzare l'elenco per implementare vari metodi.
Jon Skeet

3
@ThunderGr: Ma questo è il punto: quando non devi preoccuparti di quali altre implementazioni sono fornite dalle sottoclassi, puoi essere più libero con la tua implementazione. Ad esempio, supponiamo che un metodo venga implementato chiamando un altro metodo ed entrambi siano virtuali. Questo deve essere documentato, anche se sembra un dettaglio di implementazione. Fondamentalmente, progettare per l'eredità aggiunge le manette, che sono appropriate in alcuni casi, ma non in altri. Preferisco una classe sigillata che implementa un'interfaccia piuttosto che una classe non sigillata con un sacco di metodi virtuali.
Jon Skeet

8

Sembra che le linee guida ufficiali di Microsoft sul sigillo si siano evolute da quando questa domanda è stata posta ~ 9 anni fa, e sono passate da una filosofia di opt-in (sigillo per impostazione predefinita) a opt-out (non sigillo per impostazione predefinita):

X NON sigillare le classi senza avere una buona ragione per farlo.

Sigillare una classe perché non riesci a pensare a uno scenario di estensibilità non è una buona ragione. Agli utenti di Framework piace ereditare dalle classi per vari motivi non ovvi, come l'aggiunta di membri di convenienza. Vedere Classi non sigillate per esempi di motivi non ovvi che gli utenti desiderano ereditare da un tipo.

Buoni motivi per suggellare una classe includono quanto segue:

  • La classe è una classe statica. Vedere Static Class Design.
  • La classe archivia i segreti sensibili alla sicurezza in membri protetti ereditati.
  • La classe eredita molti membri virtuali e il costo di sigillarli individualmente supererebbe i vantaggi di lasciare la classe non sigillata.
  • La classe è un attributo che richiede una ricerca di runtime molto veloce. Gli attributi sigillati hanno livelli di prestazioni leggermente superiori rispetto a quelli non sigillati. Vedi Attributi.

X NON dichiarare membri protetti o virtuali sui tipi sealed.

Per definizione, i tipi sealed non possono essere ereditati. Ciò significa che i membri protetti sui tipi sigillati non possono essere chiamati e i metodi virtuali sui tipi sigillati non possono essere sostituiti.

✓ CONSIDERATE i membri di suggellamento che ignorate. I problemi che possono derivare dall'introduzione di membri virtuali (discussi in Membri virtuali) si applicano anche alle sostituzioni, sebbene in misura leggermente inferiore. Sigillare un override ti protegge da questi problemi a partire da quel punto nella gerarchia di ereditarietà.

In effetti, se si cerca nella base di codice ASP.Net Core , si trovano solo circa 30 occorrenze sealed class, la maggior parte delle quali sono attributi e classi di test.

Penso che la conservazione dell'immutabilità sia un buon argomento a favore della sigillatura.


4

Ho trovato questa frase nella documentazione di msdn: "Le classi Sealed vengono utilizzate principalmente per impedire la derivazione. Poiché non possono mai essere utilizzate come classe base, alcune ottimizzazioni in fase di esecuzione possono rendere la chiamata dei membri della classe Sealed leggermente più veloce."

Non so se la prestazione sia l'unico vantaggio delle classi sigillate e personalmente vorrei conoscere anche altri motivi ...


4
Sarebbe interessante vedere di che tipo di vantaggio stanno parlando ...
mmiika

3

Le prestazioni sono un fattore importante, ad esempio, la classe stringa in java è finale (<- sealed) e la ragione di ciò è solo la prestazione. Penso che un altro punto importante sia evitare il fragile problema della classe base descritto in dettaglio qui: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Se fornisci un framework, è importante per i progetti legacy di manutenibilità e per aggiornare il tuo framework per evitare il fragile problema della classe base


Il motivo per cui String in Java è definitivo non è la prestazione, è la sicurezza.
CesarB

@ CesarB: Sì, ma anche String non è una normale classe Java. È l'unica (credo) classe in Java che supporta il sovraccarico di operatori (per ulteriori informazioni, vedere qui , sezione: "Anche C e Java hanno un sovraccarico di operatori (hardcoded)"), che non è possibile in una classe normale. Per questo motivo, la Stringclasse potrebbe non essere nemmeno possibile sottoclassare, anche se non fosse definitiva.
carica il

1

Sealed viene utilizzato per prevenire il "problema della fragile classe base". Ho trovato un buon articolo in MSDN che lo spiega.


0

La sigillatura consente di realizzare alcuni piccoli miglioramenti delle prestazioni. Questo è meno vero nel mondo dei JIT e della pessimizzazione pigra che nel mondo, diciamo C ++, ma poiché .NET non è buono come la pessimazione come i compilatori java lo sono principalmente a causa di diverse filosofie di progettazione, è ancora utile. Indica al compilatore che può chiamare direttamente qualsiasi metodo virtuale piuttosto che chiamarli indirettamente tramite vtable.

È anche importante quando si desidera un "mondo chiuso" per cose come il confronto dell'uguaglianza. Normalmente, una volta definito un metodo virtuale, sono praticamente indifferente a definire una nozione di confronto di uguaglianza che implementa davvero l'idea. D'altra parte, potrei essere in grado di definirlo per una particolare sottoclasse della classe con il metodo virtuale. Sigillare quella classe assicura che l'uguaglianza regga davvero.


0

Sigillare una classe semplifica la gestione delle risorse usa e getta.


0

Per determinare se sigillare una classe, un metodo o una proprietà, dovresti generalmente considerare i seguenti due punti:

• I potenziali benefici che le classi derivate potrebbero ottenere dalla possibilità di personalizzare la propria classe.

• La possibilità che le classi derivate possano modificare le classi in modo tale che non funzionino più correttamente o come previsto.


0

Un'ulteriore considerazione è che le classi sigillate non possono essere bloccate nei test unitari. Dalla documentazione di Microsoft :

Le classi sigillate oi metodi statici non possono essere sottoposti a stub perché i tipi di stub si basano sull'invio di metodi virtuali. In questi casi, utilizzare i tipi di shim come descritto in Utilizzo di shim per isolare l'applicazione da altri assembly per i test unitari

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.