En informática , un finalizador o método de finalización es un método especial que realiza la finalización , generalmente algún tipo de limpieza. Un finalizador se ejecuta durante la destrucción del objeto , antes de que este se desasigne , y es complementario a un inicializador , que se ejecuta durante la creación del objeto , después de la asignación . Algunos desaconsejan encarecidamente el uso de finalizadores debido a la dificultad de su uso correcto y la complejidad que añaden, y sugieren alternativas, principalmente el patrón de disposición [ 1 ] (véase problemas con los finalizadores ).
El término finalizador se usa principalmente con lenguajes de programación que usan recolección de basura , como la programación orientada a objetos , arquetípicamente Smalltalk , y la programación funcional , arquetípicamente ML . Esto contrasta con un destructor , que es un método llamado para la finalización en lenguajes con ciclos de vida de objetos deterministas , arquetípicamente C++ . [ 2 ] [ 3 ] Estos son generalmente excluyentes: un lenguaje tendrá finalizadores (si la recolección de basura es automática) o destructores (si la gestión de memoria es manual), pero en casos raros un lenguaje puede tener ambos, como en C++/CLI y D , y en caso de conteo de referencias (en lugar de recolección de basura de rastreo), la terminología varía. En el uso técnico, finalizador también puede usarse para referirse a destructores, ya que estos también realizan la finalización, y se establecen algunas distinciones más sutiles – ver terminología . El término final también indica una clase que no puede ser heredada ; esto no está relacionado.
Terminología
La terminología de finalizador y finalización frente a destructor y destrucción varía entre los autores y, en ocasiones, no está clara.
En el uso común, un destructor es un método que se llama de forma determinista al destruirse un objeto, y su arquetipo son los destructores de C++; mientras que un finalizador es llamado de forma no determinista por el recolector de basura, y su arquetipo son los métodos de Javafinalize .
En los lenguajes que implementan la recolección de basura mediante conteo de referencias , la terminología varía: algunos lenguajes como Objective-C y Perl usan el destructor , y otros como Python usan el finalizador (según la especificación, Python tiene recolección de basura, pero la implementación de referencia de CPython desde su versión 2.0 usa una combinación de conteo de referencias y recolección de basura). Esto refleja que el conteo de referencias da como resultado una vida útil de los objetos semideterminista: para los objetos que no forman parte de un ciclo, los objetos se destruyen de forma determinista cuando el conteo de referencias llega a cero, pero los objetos que forman parte de un ciclo se destruyen de forma no determinista, como parte de una forma separada de recolección de basura.
En ciertos usos técnicos específicos, constructor y destructor son términos del lenguaje, que se refieren a métodos definidos en una clase , mientras que inicializador y finalizador son términos de la implementación, que se refieren a métodos llamados durante la creación o destrucción de objetos . Así, por ejemplo, la especificación original del lenguaje C# se refería a "destructores", a pesar de que C# utiliza recolección de basura, pero la especificación de la Infraestructura de Lenguaje Común (CLI) y la implementación de su entorno de ejecución, el Common Language Runtime (CLR), se referían a "finalizadores". Esto se refleja en las notas del comité del lenguaje C#, que dicen, en parte: "El compilador de C# compila los destructores para... [probablemente] instanciar finalizadores". [ 4 ] [ 5 ] Esta terminología es confusa, por lo que las versiones más recientes de la especificación de C# se refieren al método del lenguaje como "finalizadores". [ 6 ]
Otro lenguaje que no hace esta distinción terminológica es D. Aunque las clases D se recolectan como basura, sus funciones de limpieza se llaman destructores. [ 7 ]
Usar
La finalización se utiliza principalmente para la limpieza, para liberar memoria u otros recursos: para desasignar la memoria asignada mediante la gestión manual de la memoria ; para borrar referencias si se utiliza el conteo de referencias (decrementar los contadores de referencias); para liberar recursos, particularmente en el modismo de adquisición de recursos es inicialización (RAII); o para anular el registro de un objeto. La cantidad de finalización varía significativamente entre lenguajes, desde una finalización extensa en C++, que tiene gestión manual de memoria, conteo de referencias y tiempos de vida de objetos deterministas; hasta a menudo ninguna finalización en Java, que tiene tiempos de vida de objetos no deterministas y a menudo se implementa con un recolector de basura de rastreo. También es posible que haya poca o ninguna finalización explícita (especificada por el usuario), pero una finalización implícita significativa, realizada por el compilador, intérprete o tiempo de ejecución; esto es común en el caso del conteo automático de referencias, como en la implementación de referencia CPython de Python, o en el conteo automático de referencias en la implementación de Objective-C de Apple , que rompen automáticamente las referencias durante la finalización. Un finalizador puede incluir código arbitrario; Un uso particularmente complejo consiste en devolver automáticamente el objeto a un grupo de objetos .
La desasignación de memoria durante la finalización es común en lenguajes como C++, donde la gestión manual de memoria es estándar, pero también ocurre en lenguajes administrados cuando se ha asignado memoria fuera del montón administrado (externamente al lenguaje); en Java, esto sucede con la Interfaz Nativa de Java (JNI) y ByteBufferlos objetos en E/S Nueva (NIO). Esto último puede causar problemas debido a que el recolector de basura no puede rastrear estos recursos externos, por lo que no se recolectarán con la suficiente agresividad, y puede causar errores de falta de memoria debido al agotamiento de la memoria no administrada. Esto se puede evitar tratando la memoria nativa como un recurso y utilizando el patrón de disposición , como se explica más adelante.
Los finalizadores son, en general, mucho menos necesarios y se utilizan mucho menos que los destructores. Son mucho menos necesarios porque la recolección de basura automatiza la gestión de memoria , y se utilizan mucho menos porque no suelen ejecutarse de forma determinista: es posible que no se llamen a tiempo, o incluso que no se llamen en absoluto, y el entorno de ejecución no se puede predecir. Por lo tanto, cualquier limpieza que deba realizarse de forma determinista debe hacerse mediante otro método, generalmente de forma manual a través del patrón `dispose` . Cabe destacar que ni Java ni Python garantizan que los finalizadores se llamen alguna vez, por lo que no se puede confiar en ellos para la limpieza.
Debido a la falta de control del programador sobre su ejecución, se suele recomendar evitar los finalizadores salvo para las operaciones más triviales. En particular, las operaciones que se realizan habitualmente en los destructores no suelen ser apropiadas para los finalizadores. Un antipatrón común es escribir finalizadores como si fueran destructores, lo cual es innecesario e ineficaz debido a las diferencias entre ambos. Esto es especialmente frecuente entre los programadores de C++ , ya que los destructores se utilizan mucho en el lenguaje C++ estándar, siguiendo el patrón RAII ( Resource Adquisition is Initialization ).
Sintaxis
Entre los lenguajes de programación que utilizan finalizadores se incluyen C++/CLI , C# , Clean , Go , Java , JavaScript y Python . La sintaxis varía significativamente según el lenguaje.
En Java, un finalizador es un método llamado finalize, que sobrescribe el Object.finalizemétodo. [ 8 ]
En JavaScript, FinalizationRegistry permite solicitar una función de devolución de llamada cuando un objeto es recolectado por el recolector de basura.
En Python, un finalizador es un método llamado __del__.
En Perl, un finalizador es un método llamado DESTROY.

En C#, un finalizador (llamado "destructor" en versiones anteriores del estándar) es un método cuyo nombre es el nombre de la clase con ~un prefijo, como en ~Foo– esta es la misma sintaxis que un destructor de C++ , y estos métodos originalmente se llamaban "destructores", por analogía con C++, a pesar de tener un comportamiento diferente, pero se renombraron a "finalizadores" debido a la confusión que esto causaba. [ 6 ]
En C++/CLI, que tiene tanto destructores como finalizadores, un destructor es un método cuyo nombre es el nombre de la clase con ~un prefijo, como en ~Foo(como en C#), y un finalizador es un método cuyo nombre es el nombre de la clase con !un prefijo, como en !Foo.
En Go, los finalizadores se aplican a un único puntero llamando a la runtime.SetFinalizerfunción de la biblioteca estándar. [ 9 ]
Implementación
Se llama a un finalizador cuando un objeto es recolectado por el recolector de basura, es decir, después de que el objeto se ha vuelto inaccesible, pero antes de que se libere su memoria. La finalización ocurre de forma no determinista, a discreción del recolector de basura, y podría no llegar a ocurrir nunca. Esto contrasta con los destructores, que se llaman de forma determinista tan pronto como un objeto deja de utilizarse y siempre se llaman, excepto en caso de una terminación inesperada del programa. Los finalizadores suelen ser métodos de instancia , debido a la necesidad de realizar operaciones específicas de cada objeto.
El recolector de basura también debe tener en cuenta la posibilidad de resurrección de objetos. Generalmente, esto se logra ejecutando primero los finalizadores, luego verificando si algún objeto ha sido resucitado y, de ser así, abortando su destrucción. Esta verificación adicional puede ser costosa (una implementación simple vuelve a verificar toda la basura si incluso un solo objeto tiene un finalizador), lo que ralentiza y complica la recolección de basura. Por esta razón, los objetos con finalizadores pueden ser recolectados con menos frecuencia que los objetos sin finalizadores (solo en ciertos ciclos), lo que agrava los problemas causados por depender de una finalización inmediata, como las fugas de recursos.
Si un objeto se resucita, surge la cuestión de si su finalizador se vuelve a llamar cuando se destruye, ya que, a diferencia de los destructores, los finalizadores pueden llamarse varias veces. Si se llaman los finalizadores para objetos resucitados, estos pueden resucitarse repetidamente y volverse indestructibles; esto ocurre en la implementación de Python CPython anterior a Python 3.4 y en lenguajes CLR como C#. Para evitarlo, en muchos lenguajes, incluidos Java, Objective-C (al menos en las implementaciones recientes de Apple) y Python a partir de Python 3.4, los objetos se finalizan como máximo una vez, lo que requiere un seguimiento para saber si el objeto ya se ha finalizado.
En otros casos, especialmente en lenguajes CLR como C#, la finalización se gestiona por separado de los propios objetos, y estos pueden registrarse o anularse su registro repetidamente para su finalización.
Problemas
Dependiendo de la implementación, los finalizadores pueden causar una cantidad significativa de problemas y, por lo tanto, varias autoridades los desaconsejan encarecidamente. [ 10 ] [ 11 ] Estos problemas incluyen: [ 10 ]
- Es posible que los finalizadores no se llamen de manera oportuna, o que no se llamen en absoluto, por lo que no se puede confiar en ellos para mantener el estado, liberar recursos escasos o realizar cualquier otra acción importante.
- Los finalizadores pueden provocar la resurrección de objetos , lo cual suele ser un error de programación y cuya mera posibilidad ralentiza y complica significativamente la recolección de basura.
- Los finalizadores se ejecutan en función de la recolección de basura, que generalmente se basa en la presión de memoria administrada; no se ejecutan en caso de escasez de otros recursos y, por lo tanto, no son adecuados para administrar otros recursos escasos.
- Los finalizadores no se ejecutan en un orden específico y no pueden depender de invariantes de clase (ya que pueden hacer referencia a otros objetos que ya han sido finalizados).
- Los procesos de finalización lentos pueden retrasar a otros procesos de finalización.
- Por lo general, no es posible gestionar las excepciones dentro de los finalizadores, ya que el finalizador se ejecuta en un entorno no especificado, y estas excepciones pueden ignorarse o provocar la terminación incontrolada del programa.
- Los finalizadores pueden hacer referencia a objetos activos y finalizarlos accidentalmente, violando las invariantes del programa.
- Los finalizadores pueden causar problemas de sincronización, incluso en programas que de otro modo serían secuenciales (de un solo hilo), cuando la finalización se realiza en hilos separados. [ 12 ]
- Los finalizadores pueden provocar interbloqueos si se utilizan mecanismos de sincronización como los bloqueos, debido a que no se ejecutan en un orden específico y posiblemente se ejecuten simultáneamente.
- Los finalizadores que se ejecutan durante la terminación del programa no pueden depender del entorno de ejecución habitual y, por lo tanto, pueden fallar debido a suposiciones incorrectas; por esta razón, los finalizadores a menudo no se ejecutan durante la terminación.
Además, los finalizadores pueden fallar si algunos objetos permanecen accesibles incluso después de haber sido eliminados, ya sea por errores de programación o por una accesibilidad inesperada. Por ejemplo, cuando Python captura una excepción (o si no se captura en modo interactivo), mantiene una referencia al marco de pila donde se generó, lo que mantiene activos los objetos a los que se hace referencia desde ese marco de pila.
En Java, los finalizadores en una superclase también pueden ralentizar la recolección de basura en una subclase, ya que el finalizador puede potencialmente hacer referencia a campos en la subclase, y por lo tanto el campo no puede ser recolectado hasta el siguiente ciclo, una vez que el finalizador se haya ejecutado. [ 10 ] Esto se puede evitar utilizando composición en lugar de herencia .
Gestión de recursos
Un antipatrón común es usar finalizadores para liberar recursos, por analogía con el patrón RAII ( adquisición de recursos es inicialización ) de C++: adquirir un recurso en el inicializador (constructor) y liberarlo en el finalizador (destructor). Esto no funciona por varias razones. Básicamente, los finalizadores pueden no ser llamados nunca, e incluso si lo son, puede que no se llamen de manera oportuna; por lo tanto, usar finalizadores para liberar recursos generalmente causará fugas de recursos . Además, los finalizadores no se llaman en un orden preestablecido, mientras que los recursos a menudo deben liberarse en un orden específico, frecuentemente el orden opuesto al de su adquisición. Asimismo, como los finalizadores se llaman a discreción del recolector de basura, a menudo solo se llamarán bajo presión de memoria administrada (cuando hay poca memoria administrada disponible), independientemente de la presión de recursos; si los recursos escasos están ocupados por basura pero hay mucha memoria administrada disponible, la recolección de basura puede no ocurrir, por lo que no se recuperarán estos recursos.
Así, en lugar de usar finalizadores para la gestión automática de recursos, en los lenguajes con recolección de basura se deben gestionar manualmente los recursos, generalmente mediante el patrón `dispose` . En este caso, los recursos aún pueden adquirirse en el inicializador, que se llama explícitamente al instanciar el objeto, pero se liberan en el método `dispose`. El método `dispose` puede llamarse explícitamente o implícitamente mediante construcciones del lenguaje como `with-resources` de C# using, try`with-resources` de Java o `dispose` de Python with.
Sin embargo, en ciertos casos, tanto el patrón Dispose como los finalizadores se utilizan para liberar recursos. Esto se observa principalmente en lenguajes CLR como C#, donde la finalización se usa como respaldo para la eliminación: cuando se adquiere un recurso, el objeto adquirente se pone en cola para su finalización, de modo que el recurso se libera al destruirse el objeto, incluso si no se libera manualmente.
Duración de los objetos determinista y no determinista
En lenguajes con ciclos de vida deterministas para los objetos, especialmente en C++, la gestión de recursos se realiza frecuentemente vinculando el tiempo de posesión de los recursos al tiempo de vida del objeto, adquiriendo recursos durante la inicialización y liberándolos durante la finalización; esto se conoce como adquisición de recursos en la inicialización (RAII). Esto garantiza que la posesión de recursos sea una invariante de clase y que los recursos se liberen inmediatamente cuando el objeto se destruye.
Sin embargo, en lenguajes con ciclos de vida de objetos no deterministas (que incluyen todos los lenguajes principales con recolección de basura, como C#, Java y Python), esto no funciona, ya que la finalización puede no ser oportuna o no ocurrir en absoluto, y por lo tanto, los recursos pueden no liberarse durante mucho tiempo o incluso nunca, lo que provoca fugas de recursos . En estos lenguajes, los recursos generalmente se gestionan manualmente mediante el patrón `dispose` : los recursos aún pueden adquirirse durante la inicialización, pero se liberan llamando a un disposemétodo. No obstante, usar la finalización para liberar recursos en estos lenguajes es un antipatrón común , y olvidar llamar a `dispose` disposeseguirá provocando una fuga de recursos.
En algunos casos, se combinan ambas técnicas: se utiliza un método de liberación explícita, pero también se liberan los recursos que aún se mantienen durante la finalización como medida de seguridad. Esto es común en C# y se implementa registrando un objeto para su finalización cada vez que se adquiere un recurso, y suprimiendo la finalización cuando se libera dicho recurso.
Resurrección de objetos
Si se permiten finalizadores especificados por el usuario, es posible que la finalización provoque la resurrección de objetos , ya que los finalizadores pueden ejecutar código arbitrario que puede crear referencias desde objetos activos a objetos que se están destruyendo. En lenguajes sin recolección de basura, esto es un error grave que causa referencias colgantes y violaciones de seguridad de memoria ; en lenguajes con recolección de basura, esto se evita mediante el recolector de basura, generalmente agregando un paso adicional a la recolección de basura (después de ejecutar todos los finalizadores especificados por el usuario, verificar la resurrección), lo que complica y ralentiza la recolección de basura.
Además, la resurrección de objetos implica que un objeto no puede ser destruido, y en casos patológicos, un objeto puede resucitarse a sí mismo durante la finalización, volviéndose indestructible. Para evitar esto, algunos lenguajes, como Java y Python (a partir de Python 3.4), solo finalizan los objetos una vez y no finalizan los objetos resucitados. Concretamente, esto se logra mediante el seguimiento de si un objeto ha sido finalizado, objeto por objeto. Objective-C también realiza un seguimiento de la finalización (al menos en las versiones recientes de Apple ) por razones similares, tratando la resurrección como un error.
En el framework .NET , especialmente en C# y Visual Basic (.NET) , se utiliza un enfoque diferente, donde la finalización se gestiona mediante una "cola" en lugar de por objeto. En este caso, si se proporciona un finalizador especificado por el usuario, por defecto el objeto se finaliza solo una vez (se pone en cola para su finalización al crearse y se desencola una vez finalizado), pero esto se puede cambiar llamando al GCmódulo. La finalización se puede evitar llamando a GC.SuppressFinalize, que desencola el objeto, o reactivar llamando a GC.ReRegisterForFinalize, que lo encola. Esto se utiliza especialmente cuando se usa la finalización para la gestión de recursos como complemento del patrón dispose, o cuando se implementa un grupo de objetos .
Contraste con la inicialización
La finalización es formalmente complementaria a la inicialización (la inicialización ocurre al comienzo de la vida útil, la finalización al final), pero difiere significativamente en la práctica. Tanto las variables como los objetos se inicializan, principalmente para asignarles valores, pero en general solo se finalizan los objetos, y por lo general no es necesario borrar los valores: la memoria simplemente puede ser liberada y recuperada por el sistema operativo.
Más allá de asignar valores iniciales, la inicialización se utiliza principalmente para adquirir recursos o registrar un objeto con algún servicio (como un controlador de eventos ). Estas acciones tienen acciones de liberación o anulación de registro simétricas, y pueden manejarse simétricamente en un finalizador, como se hace en RAII. Sin embargo, en muchos lenguajes, especialmente en aquellos con recolección de basura, el ciclo de vida de los objetos es asimétrico: la creación de objetos ocurre de forma determinista en un punto explícito del código, pero la destrucción de objetos ocurre de forma no determinista, en un entorno no especificado, a discreción del recolector de basura. Esta asimetría implica que la finalización no puede utilizarse eficazmente como complemento de la inicialización, ya que no ocurre de manera oportuna, en un orden específico ni en un entorno específico. La simetría se restablece parcialmente al eliminar también el objeto en un punto explícito, pero en este caso la eliminación y la destrucción no ocurren en el mismo punto, y un objeto puede estar en un estado de "eliminado pero aún activo", lo que debilita las invariantes de clase y complica su uso.
Las variables generalmente se inicializan al comienzo de su ciclo de vida, pero no se finalizan al final del mismo; sin embargo, si una variable tiene un objeto como valor, dicho objeto puede finalizarse. En algunos casos, las variables también se finalizan: las extensiones de GCC permiten la finalización de variables.
Conexión confinally
Como se refleja en su denominación, finallytanto la "finalización" como la construcción cumplen propósitos similares: realizar alguna acción final, generalmente de limpieza, después de que otra cosa haya terminado. Se diferencian en el momento en que ocurren: una finallycláusula se ejecuta cuando la ejecución del programa abandona el cuerpo de la trycláusula asociada (esto ocurre durante el desenrollado de la pila, y por lo tanto hay una pila de finallycláusulas pendientes, en orden); mientras que la finalización ocurre cuando se destruye un objeto, lo cual sucede dependiendo del método de administración de memoria, y en general simplemente hay un conjunto de objetos esperando la finalización (a menudo en el montón), que no tiene por qué ocurrir en ningún orden específico.
Sin embargo, en algunos casos coinciden. En C++, la destrucción de objetos es determinista, y el comportamiento de una finallycláusula se puede generar mediante una variable local con un objeto como valor, cuyo ámbito corresponde a un bloque dentro del cuerpo de la trycláusula. El objeto se finaliza (destruye) cuando la ejecución sale de este ámbito, exactamente como si existiera una finallycláusula. Por esta razón, C++ no tiene una finallyconstrucción de finalización; la diferencia radica en que la finalización se define en la definición de la clase como el método destructor, en lugar de en el punto de llamada dentro de una finallycláusula.
Por el contrario, en el caso de una finallycláusula en una corrutina , como en un generador de Python, la corrutina puede que nunca termine —solo ceda el control— y, por lo tanto, en la ejecución ordinaria la finallycláusula nunca se ejecuta. Si se interpretan las instancias de una corrutina como objetos, entonces la finallycláusula puede considerarse un finalizador del objeto y, por lo tanto, puede ejecutarse cuando la instancia es recolectada por el recolector de basura. En la terminología de Python, la definición de una corrutina es una función generadora, mientras que una instancia de ella es un iterador generador; por lo tanto, una finallycláusula en una función generadora se convierte en un finalizador en los iteradores generadores instanciados a partir de esta función.
Historia
La noción de finalización como un paso separado en la destrucción de objetos se remonta a Montgomery (1994) , [ 13 ] por analogía con la distinción anterior de inicialización en la construcción de objetos en Martin y Odell (1992) . [ 14 ] La literatura anterior a este punto usaba "destrucción" para este proceso, sin distinguir entre finalización y desasignación, y los lenguajes de programación que datan de este período, como C++ y Perl, usan el término "destrucción". Los términos "finalizar" y "finalización" también se usan en el influyente libro Design Patterns (1994). [ a ] [ 15 ] La introducción de Java en 1995 contenía finalizemétodos, que popularizaron el término y lo asociaron con la recolección de basura, y los lenguajes a partir de este punto generalmente hacen esta distinción y usan el término "finalización", particularmente en el contexto de la recolección de basura.
Notas
- ↑ Publicado en 1994, con derechos de autor de 1995.
Referencias
- ↑ Jagger, Perry y Sestoft 2007 , p. 542 , "En C++, un destructor se llama de manera determinada, mientras que, en C# , un finalizador no lo es. Para obtener un comportamiento determinado de C#, se debe usar
Dispose. - ↑ Boehm, Hans-J. (2002). Destructores, finalizadores y sincronización . Simposio sobre principios de lenguajes de programación (POPL). Archivado del original el 8 de julio de 2015. Consultado el 7 de julio de 2015 .
- ↑ Jagger, Perry y Sestoft 2007 , p. 542 , Destructores de C++ frente a finalizadores de C# Los destructores de C++ son deterministas en el sentido de que se ejecutan en momentos conocidos, en un orden conocido y desde un hilo conocido. Por lo tanto, son semánticamente muy diferentes de los finalizadores de C#, que se ejecutan en momentos desconocidos, en un orden desconocido, desde un hilo desconocido y a discreción del recolector de basura.
- ↑ Texto completo: "Vamos a usar el término "destructor" para referirnos al miembro que se ejecuta cuando se recupera una instancia. Las clases pueden tener destructores; las estructuras no. A diferencia de C++, un destructor no se puede llamar explícitamente. La destrucción no es determinista: no se puede saber con certeza cuándo se ejecutará el destructor, salvo que se ejecuta en algún momento después de que se hayan liberado todas las referencias al objeto. Los destructores en una cadena de herencia se llaman en orden, desde el descendiente más antiguo hasta el más antiguo. No hay necesidad (ni forma) de que la clase derivada llame explícitamente al destructor de la clase base. El compilador de C# compila los destructores a la representación CLR apropiada. Para esta versión, probablemente signifique un finalizador de instancia que se distingue en los metadatos. Es posible que CLR proporcione finalizadores estáticos en el futuro; no vemos ningún obstáculo para que C# utilice finalizadores estáticos.", 12 de mayo de 1999.
- ↑ ¿Cuál es la diferencia entre un destructor y un finalizador? , Eric Lippert, Blog de Eric Lippert: Fabulosas aventuras en la programación, 21 de enero de 2010
- 1 2 Jagger, Perry y Sestoft 2007 , pág. 542 , "En la versión anterior de este estándar, lo que ahora se denomina "finalizador" se llamaba "destructor". La experiencia ha demostrado que el término "destructor" causaba confusión y a menudo generaba expectativas incorrectas, especialmente para los programadores que conocen C++. En C++, un destructor se llama de manera determinada, mientras que, en C#, un finalizador no. Para obtener un comportamiento determinado en C#, se debe usar "
Dispose. - ↑ Destructores de clases Destructores de clases en D
- ↑ java.lang, Objeto de clase: finalizar
- ↑ "Paquete de tiempo de ejecución - tiempo de ejecución - PKG.go.dev" .
- 1 2 3 " MET12-J. No utilice finalizadores ", Dhruv Mohindra, El estándar de codificación segura CERT Oracle para Java , 05. Métodos (MET) Archivado el 4 de mayo de 2014 en Wayback Machine
- ↑ object.__del__(self) , The Python Language Reference , 3. Modelo de datos : "...
__del__()los métodos deben hacer lo mínimo indispensable para mantener invariantes externos." - ↑ Hans-J. Boehm, Finalización, hilos y el modelo de memoria basado en la tecnología Java™, Conferencia JavaOne, 2005.
- ↑ Montgomery 1994 , p. 120 , "Al igual que con la instanciación de objetos, el diseño para la terminación de objetos puede beneficiarse de la implementación de dos operaciones para cada clase: una operación de finalización y una operación de terminación . Una operación de finalización rompe las asociaciones con otros objetos, asegurando la integridad de la estructura de datos."
- ↑ Montgomery 1994 , p. 119 , "Considere implementar la instanciación de clases como una operación de creación e inicialización , como sugieren Martin y Odell. La primera asigna espacio de almacenamiento para los nuevos objetos, y la segunda construye el objeto para que cumpla con las especificaciones y restricciones."
- ↑ "Cada nueva clase tiene una sobrecarga de implementación fija (inicialización, finalización, etc.)", " destructor En C++, una operación que se invoca automáticamente para finalizar un objeto que está a punto de ser eliminado."
Lecturas adicionales
- Jagger, Jon; Perry, Nigel; Sestoft, Peter (2007). Estándar de C# anotado . Morgan Kaufmann. ISBN 978-0-12-372511-0.
- Martin, James; Odell, James J. (1992). Análisis y diseño orientado a objetos . Prentice-Hall. ISBN 978-0-13-630245-2.
- Montgomery, Stephen (24 de enero de 1994). Ingeniería de la información orientada a objetos: análisis, diseño e implementación . Academic Press. ISBN 978-0-12-505040-1.
Enlaces externos
- " Finalizar en lugar de usar un destructor adecuado ", WikiWikiWeb – comparación de finalizadores de Java con destructores de C++
- Krill, Paul; " Oracle recomienda eliminar el finalizador de objetos de Java ", JavaWorld , 29 de marzo de 2017.
- Gestión de la memoria
- Método (programación informática)
- Programación orientada a objetos