2009-05-29 6 views
9

Y a-t-il des fuites de mémoire lorsque je lance une exception d'un constructeur comme suit?Lancement d'exceptions d'un constructeur dans .NET

class Victim 
{ 
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj"; 

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

Les objets défaillants seront-ils collectés par le garbage collector?

+0

A peine liée, mais bon conseil: Soyez prudent sur les exceptions lancées dans les constructeurs de contrôles. Il peut casser le concepteur pour le contrôle/formulaire. Je l'ai contourné en ayant une méthode Initialise() et en l'appelant en externe (mais je ne l'aime pas). –

Répondre

23

En général, ceci est sûr du point de vue de la non-fuite de la mémoire. Mais lancer des exceptions à partir d'un constructeur est dangereux si vous allouez des ressources non managées dans le type. Prenez l'exemple suivant

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); 
    } 
} 

Cette classe perd de la mémoire chaque fois que vous essayez de créer même si vous utilisez un bloc using. Par exemple, cela fuit la mémoire.

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

Nécessité de préciser que non seulement le corps de l'instruction using ne s'exécute pas ... mais que la méthode .Dispose() de Foo n'est pas appelée, car aucune instance n'a été créée pour appeler .Dispose() on. – jrista

+0

@jrista mis à jour – JaredPar

+0

Je suis sérieusement en retard à la fête ici, mais pas un essai-finalement dans le constructeur résoudre ce problème sans problème? – Gusdor

2

Oui, le garbage collector récupèrera les ressources gérées déjà allouées dans l'objet. Si vous avez initialisé des ressources non gérées, vous devrez les nettoyer vous-même de la manière habituelle.

1

Cela dépend des autres ressources que vous avez acquises avant que l'exception ne soit traitée. Je ne pense pas que lancer des exceptions dans un constructeur est génial, mais les jeter dans les finaliseurs ou les éliminer() est bien pire.

+2

"les jeter dans les finaliseurs ou les éliminer() est bien pire". Il peut être acceptable de jeter dans Dispose - par exemple FileStream lancera s'il est incapable de vider le Stream (par exemple en raison d'une erreur de réseau). Évitez de lancer si vous le pouvez, mais faites-le si vous le devez. Dans le cas de FileStream, avaler l'exception serait beaucoup plus dangereux que jeter, car cela donnerait à l'appelant l'impression que le fichier a été écrit avec succès. – Joe

4

Drôle, parce que j'ai aidé avec un similar question hier.

C'est un problème plus important si vous avez un type dérivé, car certaines parties du type dérivé vont s'initialiser mais pas d'autres. Du point de vue de la mémoire, cela n'a pas vraiment d'importance, parce que le garbage collector sait ce qu'il y a là. Mais si vous avez des ressources non gérées (implémenter IDisposable), les choses peuvent devenir troubles.

9

Le lancement d'exceptions à partir d'un constructeur devrait se passer correctement si vous n'avez créé aucune ressource non managée. Cependant, si vous créez des ressources non managées dans le constructeur, le corps entier de ce constructeur, y compris les throws, devrait être enveloppé dans un try/catch. Pour voler grand exemple de 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); 
    } 
} 

Seraient maintenant travailler:

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