Domanda: Perché Java / C # non può implementare RAII?
Chiarimento: sono consapevole che il garbage collector non è deterministico. Pertanto, con le funzionalità della lingua corrente, non è possibile chiamare automaticamente il metodo Dispose () di un oggetto all'uscita dall'ambito. Ma si potrebbe aggiungere una tale caratteristica deterministica?
La mia comprensione:
Ritengo che un'implementazione di RAII debba soddisfare due requisiti:
1. La durata di una risorsa deve essere vincolata a un ambito.
2. Implicito. La liberazione della risorsa deve avvenire senza una dichiarazione esplicita da parte del programmatore. Analogo a un garbage collector che libera memoria senza un'istruzione esplicita. L '"implicazione" deve avvenire solo nel punto di utilizzo della classe. Il creatore della libreria di classi deve ovviamente implementare esplicitamente un metodo distruttore o Dispose ().
Java / C # soddisfa il punto 1. In C # una risorsa che implementa IDisposable può essere associata a un ambito "using":
void test()
{
using(Resource r = new Resource())
{
r.foo();
}//resource released on scope exit
}
Ciò non soddisfa il punto 2. Il programmatore deve esplicitamente legare l'oggetto a uno speciale ambito "utilizzo". I programmatori possono (e fare) dimenticare di legare esplicitamente la risorsa a un ambito, creando una perdita.
In effetti i blocchi "using" vengono convertiti in codice try-finally-dispose () dal compilatore. Ha la stessa natura esplicita del modello try-finally-dispose (). Senza un rilascio implicito, l'hook a un ambito è lo zucchero sintattico.
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!!!
Penso che valga la pena creare una funzione di linguaggio in Java / C # che consenta oggetti speciali agganciati allo stack tramite un puntatore intelligente. La funzione ti consentirebbe di contrassegnare una classe come associata all'ambito, in modo che venga sempre creata con un gancio nello stack. Potrebbero esserci opzioni per diversi tipi di puntatori intelligenti.
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.
Penso che l'implicazione sia "ne vale la pena". Proprio come l'implicazione della raccolta dei rifiuti vale "la pena". I blocchi espliciti che utilizzano sono rinfrescanti per gli occhi, ma non offrono alcun vantaggio semantico rispetto a try-finally-dispose ().
È impraticabile implementare tale funzionalità nei linguaggi Java / C #? Potrebbe essere introdotto senza rompere il vecchio codice?
using
l'esecuzione di Dispose
è garantita (beh, lo sconto del processo muore improvvisamente senza che venga generata un'eccezione, a quel punto presumibilmente tutta la pulizia diventa discutibile).
struct
), ma sono in genere evitato se non in casi molto particolari. Vedi anche .
Dispose
i vengano mai eseguiti, indipendentemente da come vengono attivati. L'aggiunta della distruzione implicita alla fine del campo di applicazione non aiuta.