2010-01-27 13 views
6

Por ejemplo, en el código siguiente se creará un 'image'object y luego basura recogida en algún momento desconocido en el futuro¿Es posible escribir C# para que los objetos sean basura recolectada cuando quedan fuera del alcance?

void MyFunction() { 

    Bitmap image = RetrieveImage(); 
    DoSomething(image); 
} 

¿Qué hay de

void MyFunction() { 

    DoSomething(RetrieveImage()); 

} 

En este caso es el objeto basura recogida una vez que se mueve fuera del alcance, es decir, después del final de MyFunction. Si no, ¿hay algún lugar para hacer cumplir esto?

+3

no veo ninguna diferencia entre 1 und 2. ¿Por qué debe comportarse colección en cuanto distinta de la basura? – flq

+2

no soy realmente un defensor de los comentarios del tipo "¿qué quieres?", Pero en serio, el recolector de basura .NET realmente sabe mejor que el programador cuando es un buen momento para hacer una limpieza. tenga en cuenta que .NET VM no es un sistema en tiempo real. Además, si tiene recursos no administrados, use IDisposable y use. –

+0

Debe confiar en el recolector de basura estándar: es muy probable que pierda rendimiento si fuerce la recolección de basura. ¿Hay alguna razón en particular por la que le preocupa cuándo ocurre? – jball

Respuesta

19

No. De hecho, realmente no desea que se recoja basura, lo que provocará que el recolector de basura con mucha frecuencia reduzca el rendimiento.

Lo que hace desea es disponer de los recursos no administrados de manera oportuna - y ahí es donde IDisposable entra, junto con el using declaración:

void MyFunction() 
{ 
    using (Bitmap image = RetrieveImage()) 
    { 
     DoSomething(image); 
    } 
} 

que llamará image.Dispose() ya que deja el using declaración, ya sea o no DoSomething lanzó una excepción.

Usted tiene que usar la variable adicional, aunque - a menos que cambie DoSomething tomar un Func<Bitmap> lugar, por lo que en lugar de:

void DoSomething(Bitmap image) 
{ 
    // Code here 
} 
... 
DoSomething(RetrieveImage()); 

usted tendría que:

void DoSomething(Func<Bitmap> imageProvider) 
{ 
    using (Bitmap image = imageProvider()) 
    { 
     // Code here 
    } 
} 
... 
DoSomething(() => RetrieveImage()); 

nota de que ese no da la oportunidad de pasar en un mapa de bits sin que se desecha, lo que podría ser un problema si desea volver a utilizarlo más tarde. Aún así, es una buena técnica al menos saberlo.

EDITAR: Como mbeckish ha señalado en sus comentarios, no hay mucho beneficio aquí sobre la eliminación del mapa de bits dentro de RetrieveImage. Aquí hay una variante en el patrón sin embargo:

public void ApplyToEachLineInFile(string file, Action<string> action) 
{ 
    using (TextReader reader = File.OpenText(file)) 
    { 
     string line; 
     while ((line = reader.ReadLine()) != null) 
     { 
      action(line); 
     } 
    } 
} 

Aquí se encapsula la lógica "adquirir y enajenar", sin que la persona que llama la preocupación de ella - pero la persona que llama todavía puede ser muy flexible en términos de la complejidad de la lógica que pasan en.

+0

Buena sugerencia de pasar el delegado del proveedor. Ese es un patrón muy útil en muchos casos. –

+0

¿Por qué es mejor que DoSomething (RetrieveImage()); y descarte el mapa de bits en DoSomething? – mbeckish

+0

Porque si el mapa de bits se transfiere a DoSomething, ¿cómo sabe DoSomething si es seguro deshacerse de él? ¿Qué pasa si la persona que llama quería pasar el mapa de bits a DoSomethingElse después de pasarlo a DoSomething? –

4

La recolección de basura no ocurre cuando un objeto se sale del alcance. Más bien es una función de gestión de memoria automática desde el marco.

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

Puede forzar .Net para recoger la basura, pero a menos que usted está experimentando problemas graves de memoria, no lo recomendaría ir por ese camino.

+0

De acuerdo, generalmente es mejor dejar que el recolector de basura realice sus propias tareas automáticamente en lugar de invocarlo manualmente. –

0

Puede aplicar Colección de basura con:

System.GC.Collect()

después de desechar correctamente (ver Jons respuesta para el uso de() {} - Sintaxis).

< edición>

No se olvide (como acabo de hacer)

System.GC.WaitForPendingFinalizers();

después.

</edit>

Aunque no se recomienda - como dijo Jon.

+1

Calling Collect no * garantiza * que un objeto que está fuera del alcance realmente se recopile. Hay muchos escenarios interesantes y extraños en los que un objeto así puede vivir un tiempo más. También tenga en cuenta que, por lo general, debe esperar la ejecución de finalizadores pendientes si está forzando una colección; los finalizadores pendientes se ejecutan en otro hilo, por lo que podría haber carreras. –

+0

Es cierto. Gracias.Debería simplemente haber copiado el código que usamos;) – Leonidas

3

NOTA: No desea hacer esto. El recolector de basura .NET es "más inteligente" que usted (o yo, o Jon Skeet o cualquier otra persona).

try 
{ 
    Bitmap image = RetrieveImage(); 
    DoSomething(image); 
} 
finally 
{ 
    GC.Collect(); 
} 

(esto es suponiendo que ninguno de los objetos que caen fuera del ámbito implementar IDisposable)

+1

Tenga en cuenta que para que esto funcione es crucial que la variable 'image' sea declarada dentro de la declaración' try', como se da. De lo contrario, el tiempo de ejecución puede o no considerar que el objeto es inalcanzable, y puede terminar promocionándolo a una generación superior, evitando efectivamente que el objeto sea recogido de forma innecesaria. –

Cuestiones relacionadas