Pregunta: ¿Por qué Java / C # no puede implementar RAII?
Aclaración: Soy consciente de que el recolector de basura no es determinista. Entonces, con las características del lenguaje actual, no es posible llamar automáticamente al método Dispose () de un objeto al salir del ámbito. Pero, ¿podría agregarse una característica tan determinista?
Mi entendimiento:
Creo que una implementación de RAII debe cumplir dos requisitos:
1. La vida útil de un recurso debe estar vinculada a un alcance.
2. Implícito. La liberación del recurso debe ocurrir sin una declaración explícita del programador. Análogo a un recolector de basura que libera memoria sin una declaración explícita. La "implicidad" solo necesita ocurrir en el punto de uso de la clase. El creador de la biblioteca de clases, por supuesto, debe implementar explícitamente un destructor o el método Dispose ().
Java / C # satisface el punto 1. En C #, un recurso que implementa IDisposable puede vincularse a un alcance "en uso":
void test()
{
using(Resource r = new Resource())
{
r.foo();
}//resource released on scope exit
}
Esto no satisface el punto 2. El programador debe vincular explícitamente el objeto a un alcance especial "de uso". Los programadores pueden (y lo hacen) olvidar vincular explícitamente el recurso a un ámbito, creando una fuga.
De hecho, el compilador convierte los bloques "en uso" en código try-finally-dispose (). Tiene la misma naturaleza explícita del patrón try-finally-dispose (). Sin un lanzamiento implícito, el gancho a un alcance es el azúcar sintáctico.
void test()
{
//Programmer forgot (or was not aware of the need) to explicitly
//bind Resource to a scope.
Resource r = new Resource();
r.foo();
}//resource leaked!!!
Creo que vale la pena crear una función de lenguaje en Java / C # que permita objetos especiales que se enganchan a la pila mediante un puntero inteligente. La característica le permitiría marcar una clase como vinculada al alcance, de modo que siempre se cree con un gancho en la pila. Podría haber opciones para diferentes tipos de punteros inteligentes.
class Resource - ScopeBound
{
/* class details */
void Dispose()
{
//free resource
}
}
void test()
{
//class Resource was flagged as ScopeBound so the tie to the stack is implicit.
Resource r = new Resource(); //r is a smart-pointer
r.foo();
}//resource released on scope exit.
Creo que lo implícito "vale la pena". Del mismo modo que la implicidad de la recolección de basura "vale la pena". El uso explícito de bloques es refrescante para los ojos, pero no ofrece ninguna ventaja semántica sobre try-finally-dispose ().
¿No es práctico implementar tal característica en los lenguajes Java / C #? ¿Se podría introducir sin romper el código antiguo?
using
la ejecución de Dispose
está garantizada (bueno, descontando el proceso de morir repentinamente sin que se produzca una excepción, en ese momento toda la limpieza presumiblemente se vuelve discutible).
struct
), pero por lo general se evitan excepto en casos muy especiales. Ver también .
Dispose
s se ejecuten nunca , independientemente de cómo se activen. Agregar destrucción implícita al final del alcance no ayudará.