¿Por qué sellar una clase?

96

Me gustaría escuchar cuál es la motivación detrás de la mayor parte de las clases selladas en el marco .Net. ¿Cuál es el beneficio de sellar una clase? No puedo entender cómo no permitir la herencia puede ser útil y probablemente no sea el único que lucha contra estas clases.

Entonces, ¿por qué el marco está diseñado de esta manera y no sería un cambio inquebrantable abrir todo? ¿Debe haber otra razón más que ser malvado?

mmiika
fuente

Respuestas:

41
  • A veces, las clases son demasiado valiosas y no están diseñadas para ser heredadas.
  • Runtime / Reflection puede hacer suposiciones de herencia sobre clases selladas al buscar tipos. Un gran ejemplo de esto es: se recomienda sellar los atributos para la velocidad de ejecución de búsqueda. type.GetCustomAttributes (typeof (MyAttribute)) funcionará significativamente más rápido si MyAttribute está sellado.

El artículo de MSDN para este tema es Limitación de la extensibilidad mediante clases de sellado .

CVertex
fuente
3
Me alegra ver que ahora dicen claramente "úselo con precaución" ... aunque desearían practicar lo que predican.
mmiika
13
Eso me parece un mal consejo :(
Jon Skeet
4
@CVertex: Lo siento, no estaba tratando de criticarte, solo el artículo.
Jon Skeet
16
@generalt: Creo en diseñar para herencia o prohibirlo. Diseñar para la herencia requiere bastante trabajo y, a menudo, limitará las implementaciones en el futuro. La herencia también introduce incertidumbre en las personas que llaman sobre exactamente a qué llamarán. Tampoco se combina bien con la inmutabilidad (de la que soy fan). Solo encuentro útil la herencia de clases en un número relativamente pequeño de lugares (mientras que me encantan las interfaces).
Jon Skeet
1
@CVertex Si ha utilizado .NET probablemente se haya encontrado con el problema y no se haya dado cuenta, casi todas las clases principales de .NET están selladas.
CoryG
103

Las clases deben estar diseñadas para la herencia o prohibirla. El diseño de la herencia tiene un costo:

  • Puede precisar su implementación (debe declarar qué métodos van a llamar a qué otros métodos, en caso de que un usuario anule uno pero no el otro)
  • Revela su implementación en lugar de solo los efectos
  • Significa que tienes que pensar en más posibilidades al diseñar
  • Cosas como Equals son difíciles de diseñar en un árbol de herencia
  • Requiere más documentación
  • Un tipo inmutable que está subclasificado puede volverse mutable (ick)

El artículo 17 de Effective Java entra en más detalles sobre esto; independientemente del hecho de que esté escrito en el contexto de Java, el consejo también se aplica a .NET.

Personalmente, desearía que las clases estuvieran selladas por defecto en .NET.

Jon Skeet
fuente
26
Hmm ... si extiendes una clase, ¿no es tu problema si la rompes?
mmiika
25
¿Qué pasa si un cambio de implementación sobre el que no tiene control en la clase base lo rompe? ¿De quién es la culpa? La herencia introduce fragilidad, básicamente. Favorecer la composición sobre la herencia promueve la robustez, en mi opinión.
Jon Skeet
6
Sí, las interfaces son agradables, y sí, puedes favorecer la composición de todos modos. Pero si expongo una clase base sin sellar sin pensar mucho en ello, debo esperar que los cambios puedan romper las clases derivadas. Eso me parece algo malo. Mejor sellar la clase y evitar roturas, en mi opinión.
Jon Skeet
8
@Joan: La composición es una relación "tiene-a" en lugar de "es-a". Entonces, si desea escribir una clase que pueda actuar como una lista de algunas maneras, pero no en otras, es posible que desee crear una clase con una variable miembro List <T>, en lugar de derivar de List <T>. Luego usaría la lista para implementar varios métodos.
Jon Skeet
3
@ThunderGr: Pero ese es mi punto: cuando no necesita preocuparse por qué otras implementaciones proporcionan las subclases, puede ser más libre con su propia implementación. Por ejemplo, suponga que un método se implementa llamando a otro método y ambos son virtuales. Eso debe documentarse, aunque se sienta como un detalle de implementación. Básicamente, diseñar para la herencia agrega esposas, que son apropiadas en algunos casos, pero no en otros. Prefiero tener una clase sellada que implemente una interfaz que una clase sin sellar con un montón de métodos virtuales.
Jon Skeet
8

Parece que las pautas oficiales de Microsoft sobre el sellado han evolucionado desde que se hizo esta pregunta hace ~ 9 años, y pasaron de una filosofía de inclusión voluntaria (sello por defecto) a exclusión voluntaria (no sellar por defecto):

X NO selle las clases sin tener una buena razón para hacerlo.

Sellar una clase porque no puede pensar en un escenario de extensibilidad no es una buena razón. A los usuarios de framework les gusta heredar de las clases por varias razones no obvias, como agregar miembros de conveniencia. Consulte Clases sin sellar para ver ejemplos de motivos no obvios por los que los usuarios quieren heredar de un tipo.

Entre las buenas razones para sellar una clase se incluyen las siguientes:

  • La clase es una clase estática. Consulte Diseño de clases estáticas.
  • La clase almacena secretos sensibles a la seguridad en miembros protegidos heredados.
  • La clase hereda muchos miembros virtuales y el costo de sellarlos individualmente superaría los beneficios de dejar la clase sin sellar.
  • La clase es un atributo que requiere una búsqueda en tiempo de ejecución muy rápida. Los atributos sellados tienen niveles de rendimiento ligeramente más altos que los no sellados. Ver atributos.

X NO declare miembros protegidos o virtuales en tipos sellados.

Por definición, los tipos sellados no se pueden heredar. Esto significa que los miembros protegidos en tipos sellados no se pueden llamar y los métodos virtuales en tipos sellados no se pueden invalidar.

✓ CONSIDERE sellar los miembros que anula. Los problemas que pueden resultar de la introducción de miembros virtuales (discutidos en Miembros virtuales) también se aplican a las anulaciones, aunque en un grado ligeramente menor. Sellar una anulación lo protege de estos problemas a partir de ese punto en la jerarquía de herencia.

De hecho, si busca en la base de código ASP.Net Core , solo encontrará alrededor de 30 ocurrencias sealed class, la mayoría de las cuales son atributos y clases de prueba.

Creo que la conservación de la inmutabilidad es un buen argumento a favor del sellado.

Ohad Schneider
fuente
4

Encontré esta oración en la documentación de msdn: "Las clases selladas se usan principalmente para evitar la derivación. Debido a que nunca se pueden usar como una clase base, algunas optimizaciones en tiempo de ejecución pueden hacer que llamar a miembros de clase sellados sea un poco más rápido".

No sé si la actuación es la única ventaja de las clases selladas y personalmente también me gustaría conocer otras razones ...

bruno conde
fuente
4
Sería interesante ver de qué tipo de beneficio de rendimiento están hablando ...
mmiika
3

El rendimiento es un factor importante, por ejemplo, la clase de cadena en Java es final (<- sellada) y la razón de esto es solo el rendimiento. Creo que otro punto importante es evitar el problema de la clase base frágil que se describe en detalle aquí: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Si proporciona un marco, es importante para los proyectos heredados de mantenibilidad y para actualizar su marco para evitar el problema de la clase base frágil

Peter Parker
fuente
La razón por la que String en Java es final no es el rendimiento, es la seguridad.
CesarB
@CesarB: Sí, pero también, String no es una clase Java normal. Es la única (creo) clase en Java que admite la sobrecarga de operadores (para obtener más información, consulte aquí la sección: "Incluso C y Java tienen una sobrecarga de operadores (codificada)"), lo que no es posible en una clase normal. Debido a esto, Stringes posible que la clase ni siquiera sea posible subclasificar, incluso si no fuera final.
wchargin
1

Sellado se utiliza para prevenir el "problema de la clase base frágil". Encontré un buen artículo en MSDN que lo explica.

ihebiheb
fuente
0

El sellado le permite obtener algunas ganancias menores en el rendimiento. Esto es menos cierto en el mundo de los JIT y la pesimización perezosa que en el mundo de, digamos C ++, pero dado que .NET no es tan bueno como la pesimización como lo son los compiladores java principalmente debido a diferentes filosofías de diseño, sigue siendo útil. Le dice al compilador que puede llamar directamente a cualquier método virtual en lugar de llamarlo indirectamente a través de vtable.

También es importante cuando desea un 'mundo cerrado' para cosas como la comparación de igualdad. Normalmente, una vez que defino un método virtual, estoy bastante cansado de definir una noción de comparación de igualdad que realmente implemente la idea. Por otro lado, podría definirlo para una subclase particular de la clase con el método virtual. Sellar esa clase asegura que la igualdad realmente se mantenga.

Edward KMETT
fuente
0

Sellar una clase facilita la gestión de los recursos disponibles.

Jeff Dunlop
fuente
0

Para determinar si sellar una clase, método o propiedad, generalmente debe considerar los dos puntos siguientes:

• Los beneficios potenciales que pueden obtener las clases derivadas a través de la capacidad de personalizar su clase.

• La posibilidad de que las clases derivadas modifiquen sus clases de tal manera que ya no funcionen correctamente o como se esperaba.

user3629577
fuente
0

Otra consideración es que las clases selladas no se pueden apuntar en sus pruebas unitarias. De la documentación de Microsoft :

Las clases selladas o los métodos estáticos no se pueden apuntar porque los tipos de apéndices se basan en el envío de métodos virtuales. Para tales casos, use los tipos de calzas como se describe en Uso de calzas para aislar su aplicación de otros ensamblajes para pruebas unitarias

Dave Clark
fuente