c# - tipos - ¿Por qué desaparece el constructor sin parámetros predeterminado cuando se crea uno con parámetros?
tipos de constructores (10)
En C #, C ++ y Java, cuando crea un constructor que toma parámetros, el parámetro sin parámetros desaparece. Siempre acabo de aceptar este hecho, pero ahora comencé a preguntarme por qué.
¿Cuál es la razón de este comportamiento? ¿Es solo una "medida de seguridad / adivinar" que dice "Si has creado un constructor propio, probablemente no quieras que este implícito se quede"? ¿O tiene una razón técnica que hace imposible que el compilador agregue uno una vez que haya creado un constructor usted mismo?
Ciertamente no hay ninguna razón técnica por la que el lenguaje deba diseñarse de esta manera.
Hay cuatro opciones algo realistas que puedo ver:
- Sin constructores por defecto en absoluto
- El escenario actual
- Siempre proporcionando un constructor predeterminado de forma predeterminada, pero permitiendo que se suprima explícitamente
- Siempre proporcionando un constructor por defecto sin permitir que sea suprimido
La opción 1 es algo atractiva, ya que cuanto más codigo, con menor frecuencia, realmente quiero un constructor sin parámetros. Algún día debería contar la frecuencia con la que termino usando un constructor predeterminado ...
Opción 2 Estoy bien con.
La opción 3 va en contra del flujo de Java y C #, para el resto del lenguaje. Nunca hay algo que "elimine" explícitamente, a menos que cuente explícitamente haciendo las cosas más privadas de lo que serían de forma predeterminada en Java.
La opción 4 es horrible: absolutamente desea forzar la construcción con ciertos parámetros. ¿Qué significaría el new FileStream()
?
Entonces, básicamente, si acepta la premisa de que proporcionar un constructor predeterminado tiene sentido en absoluto, creo que tiene mucho sentido suprimirlo tan pronto como proporcione su propio constructor.
Creo que esto es manejado por el compilador. Si abre el ensamblado ILDASM
en ILDASM
, verá el constructor predeterminado, incluso si no está en el código. Si defines un constructor parametrizado, el constructor por defecto no se verá.
En realidad, cuando defines la clase (no estática), el compilador proporciona esta característica pensando que solo crearás una instancia. Y si desea que una operación específica funcione, seguramente tendrá su propio constructor.
Creo que la pregunta debería ser al revés: ¿por qué no necesitas declarar un constructor predeterminado si no has definido ningún otro constructor?
Un constructor es obligatorio para las clases no estáticas.
Así que creo que si no ha definido ningún constructor, el constructor predeterminado generado es solo una característica conveniente del compilador C #, también su clase no sería válida sin un constructor. Así que no hay nada de malo en generar implícitamente un constructor que no haga nada. Ciertamente se ve más limpio que tener constructores vacíos por todas partes.
Si ya ha definido un constructor, su clase es válida, entonces, ¿por qué debería el compilador asumir que quiere un constructor predeterminado? ¿Qué pasa si no quieres uno? ¿Implementar un atributo para decirle al compilador que no genere ese constructor predeterminado? No creo que sea una buena idea.
Editar. En realidad, aunque lo que digo en mi primera respuesta es válido, esta es la verdadera razón:
Al principio había C. C no está orientado a objetos (puede tomar un enfoque de OO, pero no lo ayuda ni impone nada).
Luego estaba C With Classes, que luego se renombró como C ++. C ++ está orientado a objetos y, por lo tanto, fomenta la encapsulación y garantiza la invariabilidad de un objeto: al construirse y al principio y al final de cualquier método, el objeto se encuentra en un estado válido.
Lo natural de esto es hacer que una clase siempre tenga un constructor para asegurarse de que comienza en un estado válido; si el constructor no tiene que hacer nada para garantizar esto, el constructor vacío documentará este hecho .
Pero un objetivo con C ++ era ser compatible con C al punto que tanto como sea posible, todos los programas C válidos también fueran programas C ++ válidos (ya no como un objetivo activo, y la evolución de C separada a C ++ significa que ya no es válida )
Un efecto de esto fue la duplicación en la funcionalidad entre struct
y class
. El primero haciendo las cosas a la manera C (todo lo público por defecto) y el último haciendo las cosas de una buena manera OO (todo lo privado de manera predeterminada, el desarrollador hace público públicamente lo que quiere público).
Otra es que para que una struct
C, que no podría tener un constructor porque C no tiene constructores, sea válida en C ++, entonces tenía que haber un significado para esto en la forma C ++ de mirarlo. Y así, aunque no tener un constructor iría en contra de la práctica de OO de garantizar activamente un invariante, C ++ tomó esto como que había un constructor predeterminado sin parámetros que actuaba como si tuviera un cuerpo vacío.
Todas las structs
C ahora eran structs
C ++ válidas (lo que significaba que eran las mismas que las classes
C ++ con todo, miembros y herencia, públicas) tratadas desde el exterior como si tuvieran un único constructor sin parámetros.
Sin embargo, si pones un constructor en una class
o struct
, entonces estás haciendo las cosas de la manera C ++ / OO en lugar de C, y no hay necesidad de un constructor predeterminado.
Dado que sirvió de taquigrafía, la gente siguió usándolo incluso cuando la compatibilidad no era posible de lo contrario (usaba otras características de C ++ que no estaban en C).
Por lo tanto, cuando apareció Java (basado en C ++ de muchas maneras) y más tarde C # (basado en C ++ y Java de diferentes maneras), mantuvieron este enfoque como algo a lo que los codificadores ya pueden estar acostumbrados.
Stroustrup escribe sobre esto en su lenguaje de programación C ++ y más aún, con más énfasis en los "por qué" del lenguaje en The Design and Evolution of C ++ .
=== Respuesta original ===
Digamos que esto no sucedió.
Digamos que no quiero un constructor sin parámetros, porque no puedo poner mi clase en un estado significativo sin uno. De hecho, esto es algo que puede suceder con struct
en C # (pero si no puede hacer un uso significativo de una struct
all-zeros-and-nulls en C #, en el mejor de los casos está utilizando una optimización no públicamente visible, y de lo contrario tener un defecto de diseño al usar struct
).
Para que mi clase pueda proteger sus invariantes, necesito una palabra clave removeDefaultConstructor
especial. Por lo menos, necesitaría crear un constructor privado sin parámetros para asegurarme de que ningún código de llamada llame al valor predeterminado.
Lo que complica el lenguaje un poco más. Mejor no hacerlo.
En general, es mejor no pensar en agregar un constructor como eliminar el predeterminado, mejor pensar en no tener ningún constructor como azúcar sintáctico para agregar un constructor sin parámetros que no haga nada.
El constructor predeterminado puede construirse solo cuando la clase no tiene un constructor. Los compiladores están escritos de tal manera que solo proporcionan esto como un mecanismo de respaldo.
Si tiene un constructor parametrizado, es posible que no desee que se cree un objeto utilizando el constructor predeterminado. Si el compilador hubiera proporcionado un constructor predeterminado, habría tenido que escribir un constructor no-arg y hacerlo privado para evitar que los objetos se creen sin argumentos.
Además, habrá mayores posibilidades de que olvide deshabilitar, o ''privatizar'' el constructor predeterminado, y por lo tanto causar un posible error funcional difícil de atrapar.
Y ahora tiene que definir explícitamente un constructor sin argumentos si desea que un objeto se cree ya sea de la manera predeterminada o pasando parámetros. Esto se comprueba con fuerza, y el compilador se queja de lo contrario, lo que garantiza que no haya lagunas aquí.
El constructor predeterminado sin parámetros se agrega si no hace nada usted mismo para tomar el control de la creación de objetos. Una vez que haya creado un único constructor para tomar el control, el compilador "retrocederá" y le permitirá tener el control total.
Si no fuera así, necesitarías alguna forma explícita de deshabilitar el constructor predeterminado si solo quieres que los objetos sean constructables a través de un constructor con parámetros.
Es porque cuando no define un constructor, el compilador genera automáticamente un constructor que no toma ningún argumento. Cuando quieres algo más de un constructor, lo sobrepasas. Esto NO es una sobrecarga de funciones. Entonces, el único constructor que el compilador ve ahora es el constructor que toma un argumento. Para contrarrestar este problema, puede pasar un valor predeterminado si el constructor se pasa sin valor.
Es una función de conveniencia del compilador. Si define un Constructor con parámetros pero no define un constructor sin parámetros, la posibilidad de que no desee permitir un constructor sin parámetros es mucho mayor.
Este es el caso para muchos objetos que simplemente no tienen sentido inicializar con un constructor vacío.
De lo contrario, tendrías que declarar un constructor privado sin parámetros para cada clase que quieras restringir.
En mi opinión, no es un buen estilo permitir un constructor sin parámetros para una clase que necesita parámetros para funcionar.
No hay ninguna razón para que el compilador no pueda agregar el constructor si ha agregado el suyo propio: ¡el compilador podría hacer prácticamente lo que quiera! Sin embargo, debes mirar lo que tiene más sentido:
- Si no he definido ningún constructor para una clase no estática, lo más probable es que quiera crear una instancia de esa clase. Para permitir eso, el compilador debe agregar un constructor sin parámetros, que no tendrá ningún efecto más que permitir la creación de instancias. Esto significa que no tengo que incluir un constructor vacío en mi código solo para que funcione.
- Si he definido un constructor propio, especialmente uno con parámetros, entonces probablemente tenga mi propia lógica que debe ejecutarse al crear la clase. Si el compilador fuera a crear un constructor vacío y sin parámetros en este caso, le permitiría a alguien omitir la lógica que yo escribí, lo que podría llevar a que mi código se rompa de muchas maneras. Si quiero un constructor vacío predeterminado en este caso, necesito decirlo explícitamente.
Entonces, en cada caso, puede ver que el comportamiento de los compiladores actuales tiene más sentido en términos de preservar la intención probable del código.
Premisa
Este comportamiento se puede ver como una extensión natural de la decisión de que las clases tengan un constructor público sin parámetros predeterminado . En función de la pregunta formulada, tomamos esta decisión como premisa y suponemos que no la estamos cuestionando en este caso.
Formas de eliminar el constructor predeterminado
Se deduce que debe haber una forma de eliminar el constructor público sin parámetros predeterminado. Esta eliminación se puede lograr de las siguientes maneras:
- Declarar un constructor sin nombre no público
- Eliminar automáticamente el constructor sin parámetros cuando se declara un constructor con parámetros
- Algunas palabras clave / atributos para indicar al compilador que elimine el constructor sin parámetros (lo suficientemente incómodo como para descartarlo con facilidad)
Seleccionar la mejor solución
Ahora nos preguntamos: si no hay un constructor sin parámetros, ¿por qué debe ser reemplazado? y ¿Bajo qué tipos de escenarios queremos eliminar el constructor público sin parámetros predeterminado?
Las cosas comienzan a caer en su lugar. En primer lugar, debe ser reemplazado con un constructor con parámetros o con un constructor no público. En segundo lugar, los escenarios bajo los cuales no quieres un constructor sin parámetros son:
- No queremos que la clase sea instanciada en absoluto, o queremos controlar la visibilidad del constructor: declarar un constructor no público
- Queremos forzar que se proporcionen parámetros en la construcción: declarar un constructor con parámetros
Conclusión
Ahí lo tenemos, exactamente las dos formas en que C #, C ++ y Java permiten la eliminación del constructor público sin parámetros predeterminado.