Entonces quería heredar de un sealed class
en csharp y me quemé. Simplemente no hay forma de abrirlo a menos que tenga acceso a la fuente.
Entonces me hizo pensar "¿por qué sealed
existe?". hace 4 meses. No pude resolverlo, a pesar de leer muchas cosas al respecto, como:
- Los deseos de Jon Skeet "las clases estaban selladas por defecto en .NET " .
- ¿Prefieres la composición a la herencia?
- "No debes sellar todas las clases (...)"
- ¿Cómo te burlas de una clase sellada?
He intentado digerir todo eso desde entonces, pero es demasiado para mí. Finalmente, ayer lo intenté de nuevo. Los he escaneado nuevamente, y algunos más:
- ¿Por qué una clase no debe ser "abstracta" o "final / sellada"?
- En más de 15 años de programación, la primera vez que escuché sobre SOLID , a partir de una respuesta de una pregunta ya vinculada y obviamente no lo leí todo hace 4 meses
Finalmente, después de reflexionar mucho, decidí editar en gran medida la pregunta original basada en el nuevo título.
La vieja pregunta era demasiado amplia y subjetiva. Básicamente estaba preguntando:
- En el título anterior: una buena razón para usar sellado
- En el cuerpo: ¿Cómo modificar adecuadamente una clase sellada? Olvídate de la herencia? Usar composición?
Pero ahora, entendiendo (lo que no hice ayer) que todo losealed
que hace es prevenir la herencia , y que podemos y debemos usar la composición sobre la herencia , me di cuenta de que lo que necesitaba eran ejemplos prácticos .
Supongo que mi pregunta aquí es (y de hecho siempre ha sido) exactamente lo que el Sr.Mindor me sugirió en un chat : ¿Cómo puede el diseño de la herencia causar un costo adicional?
fuente
Respuestas:
Esto no es tan difícil de comprender.
sealed
fue creado ESPECÍFICAMENTE para Microsoft con el fin de facilitarles la vida, ahorrar toneladas de dinero y ayudar a su reputación. Dado que es una función de lenguaje, todos los demás también pueden usarla, pero TÚ nunca necesitarás usar sellado.La mayoría de las personas se quejan de no poder extender una clase y si lo hacen, dicen que todos saben que es responsabilidad del desarrollador hacer que funcione correctamente. Eso es correcto, EXCEPTO que esas mismas personas no tienen idea de la historia de Windows y uno de los problemas
sealed
está tratando de resolver.Supongamos que un desarrollador extendió una clase de núcleo .Net (porque no existía sellado) y consiguió que funcionara perfectamente. Yay, para el desarrollador. El desarrollador entrega el producto al cliente. La aplicación del desarrollador funciona muy bien. El cliente esta contento. La vida es buena.
Ahora Microsoft lanza un nuevo sistema operativo, que incluye la corrección de errores en esta clase de núcleo .Net en particular. El cliente quiere mantenerse al día con los tiempos y elige instalar el nuevo sistema operativo. De repente, la aplicación que tanto le gusta al cliente ya no funciona, ya que no tuvo en cuenta los errores que se corrigieron en la clase de núcleo .Net.
¿Quién tiene la culpa?
Aquellos familiarizados con la historia de Microsoft saben que el nuevo sistema operativo de Microsoft tendrá la culpa y no el software de aplicación que hizo un mal uso de las bibliotecas de Windows. Entonces, incumbe a Microsoft solucionar el problema en lugar de la compañía de aplicaciones que lo creó. Esta es una de las razones por las cuales el código de Windows se hinchó. Por lo que he leído, el código del sistema operativo Windows está plagado de específicos if-then verifica si se está ejecutando una aplicación y versión específicas y, de ser así, realiza un procesamiento especial para permitir que el programa funcione. Microsoft gasta mucho dinero para solucionar la incompetencia de otra empresa.
El uso
sealed
no elimina completamente el escenario anterior, pero ayuda.fuente
sealed
por defecto, a menos que no estén selladas específicamente, simplemente porque muy pocas clases están realmente diseñadas para ser heredadas. Es mucho más probable que su clase no se haya creado para admitirla, y debe asegurarse de haber gastado ese tiempo de desarrollo si se esfuerza por marcarlo como no sellado.sealed
. Si de hecho fuera la opción predeterminada, significaría que si alguien heredara de un tipo, sabría que está diseñado para admitirlo.String
y luego lo mutan después del control de seguridad pero antes de la E / S? Creo que ese tipo de escenario es la razón por la cual JavaString
está marcadofinal
(similar asealed
) y apuesto a que la misma lógica sustenta algunas decisiones de C #.sealed
se usa para indicar que el escritor de la clase no lo ha diseñado para ser heredado. Nada menos, nada más.Diseñar adecuadamente una interfaz ya es bastante difícil para muchos programadores. Diseñar adecuadamente una clase que pueda ser potencialmente heredada agrega dificultad y líneas de código. Más líneas de código escritas significa más código para mantener. Para evitar esta dificultad creciente,
sealed
puede mostrar que la clase nunca fue destinada a ser heredada, y en este contexto, sellar una clase es una solución perfectamente válida para un problema .Imagine el caso de donde
CustomBoeing787
se deriva la claseAirliner
. EstoCustomBoeing787
anula algunos métodos protegidosAirliner
, pero no todos. Si el desarrollador tiene tiempo suficiente y vale la pena, puede realizar una prueba unitaria de la clase para asegurarse de que todo funcione como se espera, incluso en un contexto en el que se invocan métodos no anulados (por ejemplo, setters protegidos que cambian el estado del objeto). Si el mismo desarrollador (1) no tiene tiempo, (2) no quiere gastar cientos o miles de dólares adicionales de su jefe / cliente, o (3) no quiere escribir código adicional, no lo hace necesita ahora (YAGNI), entonces él puede:ya sea que publique el código en la naturaleza como está, teniendo en cuenta que es la preocupación de los programadores que mantendrán este proyecto más tarde para preocuparse por posibles errores,
o marque la clase
sealed
para mostrar explícitamente a los futuros programadores que esta clase no está destinada a ser heredada, y que si la abre y usa como padre, debe hacerlo bajo su propio riesgo.¿Es una buena idea sellar tantas clases como sea posible o tan pocas como sea posible? Desde mi experiencia, no hay una respuesta definitiva. Algunos desarrolladores tienden a usar
sealed
demasiado; a otros no les importa cómo se heredaría la clase y el problema que puede causar. Dado tal uso, el problema es similar al que consiste en determinar si los programadores deben usar dos o cuatro espacios para la sangría: no hay una respuesta correcta.fuente
tl;dr
aquí. Es "No, no hay una sola buena razón" o "Creo que no hay una buena razón, pero tampoco lo sé". . Para mí "ninguna respuesta definitiva" es una de esas.sealed
se usa para indicar que el escritor de la clase no lo ha diseñado para ser heredado. Esa es tu única buena razón.String
y luego lo mutan después del control de seguridad pero antes de la E / S? Creo que ese tipo de escenario es la razón por la cual JavaString
está marcadofinal
(similar asealed
) y apuesto a que la misma lógica sustenta algunas decisiones de C #.Cuando una clase está sellada, permite que el código que usa ese tipo sepa exactamente con qué están tratando.
Ahora en C #
string
essealed
, no puede heredar de él, pero supongamos por un segundo que ese no fue el caso. Si lo hiciste, alguien podría escribir una clase como esta:Esta sería ahora una clase de cadena que está violando todo tipo de propiedades que los diseñadores de
string
sabían que eran ciertas. Sabían que elEquals
método sería transitivo, esto no lo es. Demonios, este método igual no es ni siquiera simétrico, ya que una cadena podría ser igual a ella pero no sería igual a esa cadena. Estoy seguro de que hay todo tipo de programadores que han escrito código bajo el supuesto de que elToString
métodostring
no arrojará una excepción para los valores no nulos. Ahora podrá violar esa suposición.Hay muchas otras clases por las que podríamos hacer esto, y todo tipo de otros supuestos que podríamos violar. Si elimina la función de idioma para
sealed
entonces los diseñadores de idiomas ahora necesitan comenzar a contabilizar cosas como esta en todo el código de su biblioteca. Deben realizar todo tipo de comprobaciones para asegurarse de que los tipos extendidos de los tipos que desean utilizar se escriban "correctamente", lo que puede ser algo muy costoso (tanto en tiempo de desarrollo como en tiempo de ejecución). Al poder sellar una clase, pueden asegurarse de que esto no sea posible, y que cualquier afirmación que hagan sobre los detalles de implementación de sus propios tipos no se pueda violar, excepto aquellos tipos que están diseñados intencionalmente para extenderse. Para aquellos tipos diseñados para extenderse, encontrará que el código de idioma debe ser mucho más defensivo sobre cómo se trata con ellos.fuente
sealed
características incorporadas allí que no están expuestas para que las usemos. Todavía no explica por qué usarlo fuera de un alcance tan limitado y, al ser tan limitado como el código del compilador , ni siquiera explica lasealed
existencia.string
tipo no es el código del compilador. Es parte del código del idioma. Completamente diferente. En cualquier caso, acabo de elegir un ejemplo. Puede recoger la mayoría de las clases selladas en todo el BCL y usar eso como un ejemplo.Enh? No a menudo. Yo sólo se utiliza sellada cuando tengo un tipo inmutable (o alguna otra limitación) que realmente realmente necesita permanecer inmutable y no puedo herederos fiduciarios para furfill ese requisito sin introducir errores, catastropic sutiles en el sistema.
Usamos la herencia para aquellas cosas que no son predeterminadas. Incluso si la composición es favorecida sobre la herencia (y lo es), eso no significa que la herencia deba descartarse. Significa que la herencia tiene algunos problemas intrínsecos que introduce en el diseño y la capacidad de mantenimiento del código y la composición hace casi lo mismo con (en general) menos problemas. Y significa que incluso si se favorece la composición, la herencia es buena para ciertos subconjuntos de problemas.
No quiero ser demasiado malo, pero si acabas de escuchar sobre SOLID y pruebas unitarias, tal vez deberías salir de debajo de tu roca y escribir código. Las cosas no son claras, y rara vez "siempre harán X" o "nunca usarán Y" o "Z es lo mejor" será la forma correcta (como si pareciera que gravita en función de las opiniones de su pregunta). A medida que escribe el código, puede ver casos en los que
sealed
podría ser bueno y casos en los que le causa dolor, como cualquier otra compensación.fuente
sealed
, ¿por favor?En primer lugar, debería leer la publicación de Eric Lippert sobre por qué Microsoft sella sus clases.
http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx
En segundo lugar, la palabra clave sellada existe para hacer exactamente lo que hace: evitar la herencia. Hay muchas situaciones en las que desea evitar la herencia. Considere una clase de la que tenga una colección. Si su código depende de que esa clase funcione de una manera particular, no quiere que alguien anule uno de los métodos o propiedades de esa clase y haga que su aplicación falle.
En tercer lugar, el uso de sellado fomenta la composición sobre la herencia, que es un buen hábito. El uso de la composición sobre la herencia significa que no puede romper el principio de sustitución de Liskov y está siguiendo el principio Abierto / Cerrado.
sealed
es, por lo tanto, una palabra clave que lo ayuda a seguir dos de los cinco principios más importantes de la programación oo. Yo diría que eso lo convierte en una característica muy valiosa.fuente
Creo que esta pregunta no tiene una respuesta objetiva y que todo depende de su perspectiva.
Por un lado, algunas personas quieren que todo esté "abierto" y la responsabilidad de garantizar que todo funcione correctamente recae en la persona que extiende la clase. Es por eso que las clases están abiertas a la herencia por defecto. Y por qué los métodos Java son todos virtuales por defecto.
Por otro lado, algunos quieren que todo esté cerrado de forma predeterminada y proporcionar opciones para abrirlo. En este caso, es el autor de la clase base quien debe asegurarse de que todo lo que hace "abierto" sea seguro de usar de cualquier manera imaginable. Es por eso que en C # todos los métodos no son virtuales de forma predeterminada y deben declararse virtuales para ser reemplazados. También es para esas personas, que deben ser una forma de eludir los valores predeterminados "abiertos". Como hacer que la clase sea sellada / final, porque ven a alguien derivado de la clase como un gran problema para el diseño de su propia clase.
fuente
sealed
hipocresía. ¿Cómo podemos heredar de ellos? Todo lo que leo es que no podemos. Nunca. No lo sé. He estado yendo y viniendo sobre este tema durante 4 meses desde la primera vez que escuché sobre sellado. Y todavía no sé cómo hacerlo bien cuando necesito modificar algo en una clase sellada. ¡Entonces, no veo ninguna "opción para abrirlo"!El problema con sellado en C # es que es bastante final. En 7 años de desarrollo comercial de C #, el único lugar que he visto que se usa es en las clases de Microsoft .NET donde siento que tenía una razón legítima para extender una clase de marco para implementar un comportamiento ajustado, y debido a que la clase estaba sellada, no pude .
Es imposible escribir una clase pensando en todas las formas posibles en que podría usarse, y decidiendo que ninguna de ellas es legítima. Del mismo modo, si la seguridad del marco depende de que la clase no se herede, entonces tiene un defecto de diseño de seguridad.
Entonces, en conclusión, estoy firmemente convencido de que el sellado no tiene ningún propósito útil, y nada de lo que he leído aquí hasta ahora ha cambiado esa opinión.
Puedo ver que hasta ahora ha habido tres argumentos que justifican el sellado colocado hasta el momento.
El argumento 1 es que elimina una fuente de errores cuando las personas heredan de las clases y luego tienen un mayor acceso a las partes internas de esa clase sin comprender realmente las partes internas adecuadamente.
El argumento 2 es que si extiende una clase de Microsoft y luego Microsoft implementa una corrección de errores en esa clase pero su extensión se basó en ese error, entonces su código ahora está roto, Microsoft tiene la culpa, y el sellado evita que
El argumento 3 es que, como desarrollador de la clase, es mi decisión permitirle extenderlo, como parte de mi diseño de la clase.
El argumento 1 parece ser un argumento de que usted, el programador final, no sabe cómo usar mi código. Ese puede ser el caso, pero si aún me permite acceder a la interfaz de los objetos, es cierto que estoy usando su objeto y haciendo llamadas a él sin comprender cómo usarlo, por lo que puede causar el mismo daño de esa manera. . Esta es una justificación débil de la necesidad de sellado.
Entiendo de dónde viene la base fáctica para arg 2. En particular, cuando Microsoft realizó comprobaciones adicionales del sistema operativo en las llamadas a las API de win32 para identificar las llamadas malformadas, que han mejorado la estabilidad general de Windows XP en comparación con Windows NT, pero a corto plazo, muchas aplicaciones se rompieron cuando se lanzó Windows XP. Microsoft resolvió esto poniendo "reglas" para que estas verificaciones digan, ignorar cuando la aplicación X rompe esta regla, es un error conocido
El argumento 2 es un argumento pobre porque, en el mejor de los casos, sellado no hace ninguna diferencia, ya que si hay un error en la biblioteca .NET, no tiene más remedio que encontrar una solución, y cuando salga la solución de Microsoft, es muy probable significa que su trabajo que depende del comportamiento roto ahora está roto. Ya sea que haya solucionado este problema mediante el uso de la herencia o mediante algún otro código contenedor adicional, cuando el código se arregle, su solución se romperá.
El argumento 3 es el único argumento que tiene alguna sustancia para justificarse a sí mismo. Pero aún es débil. Simplemente va en contra de la reutilización del código.
fuente
It is impossible to write a class thinking about every possible way it could be used
- Esa es precisamente la razón por la cual muchas clases de Framework están selladas ... Elimina la necesidad de pensar en todas las formas posibles en que alguien podría extender la clase.Solo uso
sealed
palabras clave en clases que contienen solo métodos estáticos.fuente
static
lugar?Como es evidente por mi pregunta, no soy un experto aquí. Pero no veo ninguna razón para
sealed
existir.Parece hecho para ayudar a los arquitectos de códigos a diseñar una interfaz. Si desea escribir una clase que saldrá en libertad y muchas personas heredarán de ella, modificar cualquier cosa en esa clase puede dividirla para demasiadas personas. Entonces podemos sellarlo y nadie puede heredar. "Problema resuelto".
Bueno, parece "cubrir un lugar y descubrir otro". Y ni siquiera está resolviendo un problema, solo está eliminando una herramienta : la herencia. Como cualquier herramienta, tiene muchos usos y heredar de una clase inestable es un riesgo que corres. Quien tomó ese riesgo debería saberlo mejor.
Tal vez podría haber una palabra clave
stable
, para que las personas sepan que es más seguro heredarla.fuente
sealed
no sea una herramienta útil para los diseñadores de marcos. Las clases en un marco deben ser cajas negras; no debería tener que saber sobre los aspectos internos de una clase para poder usarla correctamente. Sin embargo, eso es exactamente lo que exige la herencia.