2009-05-29 14 views
9

Si verificano perdite di memoria quando si genera un'eccezione da un costruttore come il seguente?Eccezioni di lancio da un costruttore in .NET

class Victim 
{ 
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj"; 

    public Victim() 
    { 
     //throw new Exception("oops!"); 
    } 
} 

Saranno gli oggetti in mancanza essere raccolti dal garbage collector?

+0

Appena connessi, ma consiglio utile: Fate attenzione alle eccezioni sollevate nei costruttori di controlli. Può rompere il progettista per il controllo/modulo. L'ho preso avendo un metodo Initialise() e chiamandolo esternamente (ma non mi piace). –

risposta

23

In generale questo è sicuro dal punto di vista della perdita di memoria. Tuttavia, lanciare eccezioni da un costruttore è pericoloso se si assegnano risorse non gestite nel tipo. Prendi il seguente esempio

public class Foo : IDisposable { 
    private IntPtr m_ptr; 
    public Foo() { 
    m_ptr = Marshal.AllocHGlobal(42); 
    throw new Exception(); 
    } 
    // Most of Idisposable implementation ommitted for brevity 
    public void Dispose() { 
    Marshal.FreeHGlobal(m_ptr); 
    } 
} 

Questa classe perde memoria ogni volta che si tenta di creare anche se si utilizza un blocco utilizzando. Ad esempio, questo perde memoria.

using (var f = new Foo()) { 
    // Won't execute and Foo.Dispose is not called 
} 
+0

È necessario chiarire che non solo il corpo dell'istruzione using non viene eseguito ... ma il metodo .Dispose() di Foo non viene chiamato, perché non è stata creata alcuna istanza per chiamare .Dispose(). – jrista

+0

@jrista aggiornato – JaredPar

+0

Sono seriamente in ritardo per la festa qui, ma non ci proverei, finalmente, nel costruttore, risolvere questo problema senza problemi? – Gusdor

2

Sì, il garbage collector recupererà le risorse gestite già allocate nell'oggetto. Se hai inizializzato le risorse non gestite, dovrai pulirle da soli nel modo normale.

1

Dipende da quali altre risorse hai acquisito prima che l'eccezione sia riconosciuta. Non credo che lanciare eccezioni in un costruttore sia grandioso, ma buttarle in finalizzatori o dispose() è molto peggio.

+2

"buttarli in finalizzatori o dispose() è molto peggio.". Può essere accettabile lanciare Dispose, ad esempio FileStream verrà lanciato se non è in grado di svuotare il flusso (ad esempio a causa di un errore di rete). Evita di lanciare se puoi, ma fallo se devi. Nel caso di FileStream, l'ingerenza dell'eccezione sarebbe molto più pericolosa del lancio, poiché darebbe al chiamante l'impressione che il file fosse stato scritto correttamente. – Joe

4

Divertente, perché ho aiutato con un similar question proprio ieri.

È un problema più grande se si dispone di un tipo derivato, poiché alcune parti del tipo derivato verranno inizializzate ma non altre. Dal punto di vista della memoria, non importa, perché il garbage collector sa cosa è dove. Ma se hai risorse non gestite (implementa IDisposable) le cose possono diventare torbide.

9

Le eccezioni di lancio da un costruttore dovrebbero andare bene se non sono state create risorse non gestite. Tuttavia, se si creano risorse non gestite nel costruttore, l'intero corpo di tale costruttore, inclusi i lanci, deve essere racchiuso in un try/catch. Per rubare grande esempio di JaredPar:

public class Foo : IDisposable { 
    private IntPtr m_ptr; 
    public Foo() { 
    try 
    { 
     m_ptr = Marshal.AllocHGlobal(42); 
     throw new Exception(); 
    } 
    catch 
    { 
     Dispose(); 
     throw; 
    } 
    } 
    // Most of Idisposable implementation ommitted for brevity 
    public void Dispose() { 
    Marshal.FreeHGlobal(m_ptr); 
    } 
} 

Di seguito sarebbe ora lavorare:

using (var f = new Foo()) { 
    // Won't execute, but Foo still cleans itself up 
} 
Problemi correlati