Pourquoi sceller une classe?

96

J'aimerais savoir quelle est la motivation derrière la plupart des classes scellées dans le framework .Net. Quel est l'avantage de sceller une classe? Je ne peux pas imaginer comment ne pas autoriser l'héritage peut être utile et probablement pas le seul à lutter contre ces classes.

Alors, pourquoi le cadre est-il conçu de cette façon et ne serait-ce pas un changement inconditionnel de tout desceller? Il doit y avoir une autre raison, mais simplement être mauvais?

mmiika
la source

Réponses:

41
  • Parfois, les classes sont trop précieuses et ne sont pas conçues pour être héritées.
  • Runtime / Reflection peut faire des hypothèses d'héritage sur les classes scellées lors de la recherche de types. Un bon exemple de ceci est - Il est recommandé que les attributs soient scellés pour la vitesse d'exécution de la recherche. type.GetCustomAttributes (typeof (MyAttribute)) fonctionnera beaucoup plus rapidement si MyAttribute est scellé.

L'article MSDN de cette rubrique est la limitation de l'extensibilité en scellant les classes .

CVertex
la source
3
Heureux de voir qu'ils disent clairement «à utiliser avec prudence» maintenant ... ils aimeraient cependant pratiquer ce qu'ils prêchent.
mmiika
13
Cela me semble être un mauvais conseil :(
Jon Skeet
4
@CVertex: Désolé, je n'essayais pas de vous critiquer - juste l'article.
Jon Skeet
16
@generalt: Je crois en la conception pour l'héritage ou en l'interdisant. La conception pour l'héritage demande un peu de travail et limitera souvent les implémentations à l'avenir. L'héritage introduit également l'incertitude chez les appelants quant à savoir exactement à quoi ils vont appeler. Cela ne se mélange pas bien non plus avec l'immuabilité (dont je suis fan). Je trouve que l'héritage de classe n'est utile que dans un nombre relativement restreint d'endroits (alors que j'aime les interfaces).
Jon Skeet
1
@CVertex Si vous avez utilisé .NET, vous avez probablement rencontré le problème et que vous n'avez tout simplement pas remarqué, presque toutes les classes principales .NET sont scellées.
CoryG
103

Les classes doivent être conçues pour l'héritage ou l'interdire. Il y a un coût à concevoir pour l'héritage:

  • Il peut identifier votre implémentation (vous devez déclarer quelles méthodes vont appeler quelles autres méthodes, au cas où un utilisateur remplacerait l'une mais pas l'autre)
  • Il révèle votre implémentation plutôt que simplement les effets
  • Cela signifie que vous devez penser à plus de possibilités lors de la conception
  • Des choses comme Equals sont difficiles à concevoir dans un arbre d'héritage
  • Cela nécessite plus de documentation
  • Un type immuable qui est sous-classé peut devenir mutable (ick)

Le point 17 de Effective Java donne plus de détails à ce sujet - indépendamment du fait qu'il soit écrit dans le contexte de Java, les conseils s'appliquent également à .NET.

Personnellement, je souhaite que les classes soient scellées par défaut dans .NET.

Jon Skeet
la source
26
Hmm ... si vous prolongez une classe, n'est-ce pas votre problème si vous la cassez?
mmiika
25
Que faire si un changement d'implémentation sur lequel vous n'avez aucun contrôle dans la classe de base vous brise? A qui est-ce la faute? L'héritage introduit la fragilité, fondamentalement. Favoriser la composition plutôt que l'héritage favorise la robustesse, l'OMI.
Jon Skeet
6
Oui, les interfaces sont agréables - et oui, vous pouvez quand même privilégier la composition. Mais si j'expose une classe de base non scellée sans y penser très attentivement, je devrais m'attendre à ce que les changements puissent casser les classes dérivées. Cela me semble une mauvaise chose. Mieux vaut sceller la classe et éviter la casse, IMO.
Jon Skeet
8
@Joan: La composition est une relation "has-a" plutôt que "is-a". Donc, si vous voulez écrire une classe qui peut agir comme une liste à certains égards, mais pas à d'autres, vous pouvez créer une classe avec une variable membre List <T>, plutôt que de dériver de List <T>. Vous utiliseriez ensuite la liste pour implémenter diverses méthodes.
Jon Skeet
3
@ThunderGr: Mais c'est mon point: quand vous n'avez pas à vous soucier des autres implémentations fournies par les sous-classes, vous pouvez être plus libre avec votre propre implémentation. Par exemple, supposons qu'une méthode soit implémentée en appelant une autre méthode et que les deux soient virtuelles. Cela doit être documenté - même si cela ressemble à un détail de mise en œuvre. Fondamentalement, la conception pour l'héritage ajoute des menottes - qui sont appropriées dans certains cas, mais pas dans d'autres. Je préfère avoir une classe scellée implémentant une interface plutôt qu'une classe non scellée avec un tas de méthodes virtuelles.
Jon Skeet
8

Il semble que les directives officielles de Microsoft sur le scellement ont évolué depuis que cette question a été posée il y a environ 9 ans et qu'elles sont passées d'une philosophie d'acceptation (sceller par défaut) à se désinscrire (ne pas sceller par défaut):

X NE PAS sceller les classes sans avoir une bonne raison de le faire.

Sceller une classe parce que vous ne pouvez pas penser à un scénario d'extensibilité n'est pas une bonne raison. Les utilisateurs du framework aiment hériter des classes pour diverses raisons non évidentes, comme l'ajout de membres pratiques. Consultez Classes non scellées pour obtenir des exemples de raisons non évidentes pour lesquelles les utilisateurs souhaitent hériter d'un type.

Les bonnes raisons de sceller une classe sont les suivantes:

  • La classe est une classe statique. Voir Conception de classe statique.
  • La classe stocke les secrets sensibles à la sécurité dans les membres protégés hérités.
  • La classe hérite de nombreux membres virtuels et le coût de leur scellement individuel l'emporterait sur les avantages de laisser la classe descellée.
  • La classe est un attribut qui nécessite une recherche d'exécution très rapide. Les attributs scellés ont des niveaux de performance légèrement supérieurs à ceux non scellés. Voir les attributs.

X NE déclarez PAS de membres protégés ou virtuels sur des types scellés.

Par définition, les types scellés ne peuvent pas être hérités. Cela signifie que les membres protégés sur les types scellés ne peuvent pas être appelés et que les méthodes virtuelles sur les types scellés ne peuvent pas être remplacées.

✓ CONSIDÉREZ les éléments d'étanchéité que vous remplacez. Les problèmes qui peuvent résulter de l'introduction de membres virtuels (abordés dans Membres virtuels) s'appliquent également aux remplacements, bien qu'à un degré légèrement moindre. Sceller un remplacement vous protège de ces problèmes à partir de ce point dans la hiérarchie d'héritage.

En effet, si vous recherchez la base de code ASP.Net Core , vous ne trouverez qu'environ 30 occurrences de sealed class, dont la plupart sont des attributs et des classes de test.

Je pense que la conservation de l'immuabilité est un bon argument en faveur du scellement.

Ohad Schneider
la source
4

J'ai trouvé cette phrase dans la documentation msdn: "Les classes scellées sont principalement utilisées pour empêcher la dérivation. Comme elles ne peuvent jamais être utilisées comme classe de base, certaines optimisations d'exécution peuvent rendre l'appel des membres de classe scellés légèrement plus rapide."

Je ne sais pas si la performance est le seul avantage des classes scellées et personnellement j'aimerais aussi connaître d'autres raisons ...

bruno conde
la source
4
Ce serait intéressant de voir de quel genre d'avantage de performance ils parlent ...
mmiika
3

Les performances sont un facteur important, par exemple, la classe de chaîne en java est finale (<- scellée) et la raison en est uniquement les performances. Je pense qu'un autre point important est d'éviter le problème de classe de base fragile décrit en détail ici: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Si vous fournissez un cadre, il est important pour les projets hérités de maintenabilité et de mettre à niveau votre cadre pour éviter le problème de classe de base fragile

Peter Parker
la source
La raison pour laquelle String en java est final n'est pas la performance, c'est la sécurité.
CesarB
@CesarB: Oui, mais aussi, String n'est pas une classe Java normale. C'est la seule classe (je crois) en Java qui supporte la surcharge d'opérateurs (pour en savoir plus, voir ici , section: "Même C et Java ont une surcharge d'opérateurs (codée en dur)"), ce qui n'est pas possible dans une classe normale. Pour cette raison, la Stringclasse pourrait même ne pas être possible de sous-classe, même si elle n'était pas définitive.
wchargin
1

Scellé est utilisé pour éviter le "problème de classe de base fragile". J'ai trouvé un bon article dans MSDN qui explique cela.

ihebiheb
la source
0

L'étanchéité vous permet de réaliser des gains de performances mineurs. C'est moins vrai dans le monde des JIT et de la pessimisation paresseuse que dans le monde de, disons C ++, mais puisque .NET n'est pas aussi bon que la pessimisation que les compilateurs java le sont principalement à cause de différentes philosophies de conception, il est toujours utile. Il indique au compilateur qu'il peut appeler directement toutes les méthodes virtuelles plutôt que de les appeler indirectement via la vtable.

C'est également important lorsque vous voulez un «monde fermé» pour des choses comme la comparaison d'égalité. Normalement, une fois que j'ai défini une méthode virtuelle, je suis à peu près tenté de définir une notion de comparaison d'égalité qui implémente vraiment l'idée. D'un autre côté, je pourrais peut-être le définir pour une sous-classe particulière de la classe avec la méthode virtuelle. Sceller cette classe garantit que l'égalité tient vraiment.

Edward KMETT
la source
0

Sceller une classe facilite la gestion des ressources jetables.

Jeff Dunlop
la source
0

Pour déterminer s'il faut sceller une classe, une méthode ou une propriété, vous devez généralement considérer les deux points suivants:

• Les avantages potentiels que les classes dérivées pourraient tirer de la possibilité de personnaliser votre classe.

• La possibilité que les classes dérivées puissent modifier vos classes de telle sorte qu'elles ne fonctionneraient plus correctement ou comme prévu.

user3629577
la source
0

Une autre considération est que les classes scellées ne peuvent pas être stubbées dans vos tests unitaires. À partir de la documentation de Microsoft :

Les classes scellées ou les méthodes statiques ne peuvent pas être stubbées car les types stub reposent sur la répartition des méthodes virtuelles. Dans de tels cas, utilisez des types de shim comme décrit dans Utilisation de shims pour isoler votre application des autres assemblys pour les tests unitaires.

Dave Clark
la source