2009-05-29 22 views
9

¿Hay alguna pérdida de memoria cuando lanzo una excepción de un constructor como la siguiente?Lanzar excepciones de un constructor en .NET

class Victim 
{ 
    public string var1 = "asldslkjdlsakjdlksajdlksadlksajdlj"; 

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

¿Los objetos anómalos serán recogidos por el recolector de basura?

+0

apenas relacionado, pero consejo útil: Tenga cuidado con las excepciones producidas en los constructores de los controles. Puede romper el diseñador para el control/formulario. Lo solucioné teniendo un método Initialise() y llamándolo externamente (pero no me gusta). –

Respuesta

23

En general, esto es seguro desde la perspectiva de no tener pérdidas de memoria. Pero lanzar excepciones desde un constructor es peligroso si asigna recursos no administrados en el tipo. Tome el siguiente ejemplo

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

Esta clase perderá memoria cada vez que intente crear aunque use un bloque de uso. Por ejemplo, esto pierde memoria.

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

Es necesario dejar en claro que no solo el cuerpo de la instrucción using no se ejecuta ... sino que el método .Dispose() de Foo no se llama, porque no se creó ninguna instancia para llamar a .Dispose(). – jrista

+0

@jrista updated – JaredPar

+0

Llego muy tarde a la fiesta aquí, pero ¿no lo intentaría, finalmente en el constructor arreglar esto sin problemas? – Gusdor

2

Sí, el recolector de basura reclamará los recursos administrados ya asignados en el objeto. Si ha inicializado recursos no administrados, deberá limpiarlos usted mismo de la manera normal.

1

Depende de qué otros recursos haya adquirido antes de que se produzca la excepción. No creo que lanzar excepciones en un constructor sea grandioso, pero arrojarlos a finalizadores o deshacerse de ellos() es mucho peor.

+2

"arrojarlos a los finalizadores o deshacerse de ellos() es mucho peor". Puede ser aceptable tirar en Dispose, por ejemplo, FileStream lanzará si no puede vaciar el Stream (por ejemplo, debido a un error de red). Evite tirar si puede, pero hágalo si es necesario. En el caso de FileStream, tragar la excepción sería mucho más peligroso que tirar, ya que le daría a la persona que llama la impresión de que el archivo se ha escrito con éxito. – Joe

4

Gracioso, porque ayudé con un similar question ayer.

Es un problema mayor si tiene un tipo derivado, porque algunas partes del tipo derivado se inicializarán pero no otras. Desde la perspectiva de la memoria, realmente no importa, porque el recolector de basura sabe dónde está. Pero si tiene recursos no administrados (implementar IDisposable) las cosas pueden ponerse turbias.

9

Lanzar excepciones desde un constructor debería estar bien si no ha creado recursos no administrados. Sin embargo, si crea recursos no administrados en el constructor, todo el cuerpo de ese constructor, incluidos los throws, debería estar envuelto en un try/catch. Para robar gran ejemplo 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); 
    } 
} 

El siguiente sería ahora trabajar:

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