2009-07-30 23 views
12

Cuando usted tiene un código como:¿Cuándo es necesario deshacerse?

Bitmap bmp = new Bitmap (100, 100); 
Graphics g = Graphics.FromImage (bmp); 

Pen p = new Pen (Color.FromArgb (128, Color.Blue), 1); 
Brush b = new SolidBrush (Color.FromArgb (128, Color.Blue)); 

g.FillEllipse (b, 0, 0, 99, 99);  
g.FillRegion (b, pictureBox1.Region); 

pictureBox1.BackColor = Color.Transparent; 
pictureBox1.Image = bmp; 

¿Tiene que disponer el lápiz y el pincel? ¿Qué hay de bmp y g?

Mi pregunta principal es, si estos se eliminan manualmente, ¿por qué no se eliminan tan pronto como se salgan del alcance? ¿Eso es lo que pasaría si no los desechara manualmente? ¿Es la demora lo que hace que la gente haga esto manualmente?

Respuesta

1

Sí, bmp, g, byp son todos IDisponibles, debe desechar() todos ellos. Preferiblemente usando bloques using() {}.

Existen excepciones, cuando use Pen p2 = Pens.Blue;, no debe eliminar p2. Se llama un artículo común. Lo mismo para Brushes.Black, etc.

En cuanto al por qué, es el mismo para todas las clases desechables. .Net no utiliza el recuento de referencias por lo que no hay (no puede haber) una acción inmediata cuando una referencia queda fuera del alcance.

Y dejarlo al recolector de basura eventualmente los liberará, pero es (muy) ineficiente. Sé de una aplicación ASP.NET (!) Que falló en la escasez de controladores gráficos debido a que no los eliminó de inmediato. Estaba generando imágenes.

+0

Gracias Henk. ¿Qué sucede si llamas a Dispose on Pens.Blue? ¿Es mala? –

+1

Sabes, realmente no lo sé. Voy a adivinar antes de intentarlo: o recibes una excepción inmediatamente o no pasa nada. –

+1

Lo intenté: recibes una System.ArgumentException con el mensaje de que los cambios no están permitidos. –

1

Dispose se utiliza para deshacerse de recursos no administrados.

Así que, como regla general, guardo cualquier instancia de objetos IDisposable en una instrucción using, por lo que no tengo que preocuparme por el recurso no administrado que tiene un Pen.

17

Sí, tiene que desecharlos, no solo lápiz y brocha, sino también Bitmap y Graphics.

No se eliminan cuando están fuera del alcance porque las variables en sí mismas son referencias, no objetos, y el compilador de C# no sabe si la propiedad pertenece o no a esas referencias (por ejemplo, FillEllipse podría, en teoría, recuerde la referencia que se le da, y trate de usarla en algún momento posterior; ¡recuerde que el compilador de lenguaje no tiene ningún conocimiento especial de la semántica de la biblioteca!).

Si desea indicar que la propiedad se limita a ese ámbito, se utiliza el using declaración:

using (Bitmap bmp = new Bitmap (100, 100)) 
using (Graphics g = Graphics.FromImage (bmp)) 
using (Pen p = new Pen (Color.FromArgb (128, Color.Blue), 1)) 
using (Brush b = new SolidBrush (Color.FromArgb (128, Color.Blue))) 
{ 
    g.FillEllipse (b, 0, 0, 99, 99);  
    g.FillRegion (b, pictureBox1.Region); 
} 

Esto hará que el inserto compilador llama a Dispose automáticamente según sea necesario, asegurando que todos los objetos se disponen una vez el alcance correspondiente using queda (normalmente, mediante transferencia de control como return o break, o una excepción).

Si viene de experiencia en C++, using en C# es directamente análoga a un const std::auto_ptr, excepto que es una construcción del lenguaje aquí, y sólo se puede utilizar para las variables locales (es decir, no para campos de clase).

+1

¿De verdad? Interesante. Nunca he visto eso para nada más que el objeto Graphics antes. ¿Hay un sitio de MS que habla de esto con más detalle? – derGral

+4

Es cierto que debe llamar a 'Dispose' o emplear la instrucción' using', pero no es cierto que el 'Pen' no se eliminará fuera del alcance debido a que algunas API podrían estar usándolo. 'Pen' tiene un finalizador, que se ejecutará en algún momento no especificado después de que se vuelva inalcanzable (para un objeto efímero puede ser muy poco después), y cuando eso suceda, hará el equivalente a' Dispose'. –

+1

Ver: http://msdn.microsoft.com/en-us/library/system.drawing.pen.dispose.aspx - "call Dispose ... De lo contrario, los recursos que está utilizando no se liberarán hasta que el recolector de basura llama al método Finalize del objeto Pen ". –

3

C# no "destruye" ni elimina cosas tan pronto como salen del alcance.

Esas clases se más probable liberar automáticamente los recursos no administrados que se aferran a en su Finalizer método especial, que se llamará cuando son basura recogida en un tiempo indeterminado después de ir fuera de alcance.

Pero confiar en eso es confiar en algo que está fuera de tu control, y puede que no ocurra por un tiempo.

Si la clase implementa IDisposable, lo mejor es que llame manualmente al Dispose() en algún lugar, o envuélvala preferiblemente en un bloque using. De esta forma, puede estar seguro de que:

A. Los recursos no administrados son que se liberan definitivamente.

B. Los recursos no administrados se liberan tan pronto como sea posible.

6

Sé que otras personas han puesto ejemplos de código aquí, pero empecé así que voy a terminar:

using (Bitmap bmp = new Bitmap(100, 100)) 
{ 
    using (Graphics g = Graphics.FromImage(bmp)) 
    { 
    using (Pen p = new Pen(Color.FromArgb(128, Color.Blue), 1)) 
    { 
     using (Brush b = new SolidBrush(Color.FromArgb(128, Color.Blue))) 
     { 
     g.FillEllipse(b, 0, 0, 99, 99); 
     g.FillRegion(b, pictureBox1.Region); 

     pictureBox1.BackColor = Color.Transparent; 
     pictureBox1.Image = bmp; 
     } 
    } 
    } 
} 

siempre uso using en mi código, ya que llama Dispose() en su objeto automáticamente, incluso si hay una excepción planteada en el bloque using. Lo uso mucho para SharePoint proyectos (pero esa es otra historia ...).

+3

Correcto, pero se está convirtiendo en habitual plegar múltiples usos a 1 par de corchetes, como en la muestra de Pavels. –

+1

Genial, no he visto esa técnica antes. Creo que empezaré a utilizarlo (sin juego de palabras) a partir de ahora :) –

+0

Yester Respondí a una [pregunta] (http://stackoverflow.com/q/14946381/799558) relacionada con la eliminación de un mapa de bits después de asignarlo a ' pictureBox1.Image' y mi respuesta fue exactamente como la tuya (ya borré mi respuesta). me dijeron que la imagen también será eliminada. ¿Puedo seguir usando 'using' en esta situación? – AbZy

2

Si el patrón desechable se utiliza correctamente, es Disposeno es estrictamente necesario - que se llamará cuando se finalice el objeto, para que no se escape recursos o nada.

Sin embargo, es una buena educación llamar al Dispose tan pronto como termine de usar el objeto, ya que los desechables a menudo controlan directamente los recursos nativos que generalmente son limitados. Normalmente, los objetos no se finalizan/recogen de inmediato, por lo que esos recursos simplemente se desperdician, una vez que ya no usas el objeto. La eliminación libera esos recursos de inmediato, por lo que pueden ser utilizados por otras partes del programa (o en algunos casos, por otros programas).

Nota, un bloque using automáticamente elimina el objeto cuando haya terminado con él, por lo que rara vez o nunca ve Dispose dentro de un bloque using.

versión corta: Si el objeto implementa IDisposable, y su código creó (es decir: si no es un objeto del sistema como Pens.Blue, o la Graphics te pasó en OnPaint etc.), debe desecharse cuando se está totalmente hecho con él, ya sea llamando al Dispose, o llamando a algún otro método especificado para llamar al Dispose (Close es común), o usando un bloque using. Usted no tiene tiene para desecharlo, pero casi siempre debería hacerlo.

+1

Muchos objetos no se limpiarán por sí mismos si se abandonan. Si bien la limpieza es bastante simple en algunos casos, en otros es esencialmente imposible. A menos que uno sepa que es seguro abandonar objetos de una clase en particular, se debe suponer que abandonar objetos sin llamar a Dispose primero hará que sucedan cosas malas. – supercat

+0

Cualquier objeto que implemente IDisposable correctamente * se * limpiará si se abandona. Al igual que todos los desechables en el BCL. Cualquier objeto que no debería tener su creador encontrado y disparado, ya que no es nada difícil de hacer, en casi todos los casos, puede simplemente llamar a 'Dispose' en el finalizador, y cualquiera que se meta con recursos nativos debe ser íntimamente consciente de lo que sucede cuando no se lanzan correctamente. Cualquiera que no esté jugando directamente con recursos nativos realmente no necesita implementar IDisposable. – cHao

+0

Supongamos que uno quiere construir un ChangeCounter de clase cuyo constructor acepta un objeto INotifyPropertyChanged, y que informará cuántas veces ese objeto ha elevado su evento PropertyChanged desde que se creó (el ChangeCounter). ¿De qué manera podría una clase así limpiar si se abandona? Tenga en cuenta que INotifyPropertyChanged no promete que las implementaciones de PropertyChanged.RemoveHandler se pueden usar de forma segura desde el hilo del finalizador. – supercat

Cuestiones relacionadas