2010-03-30 20 views
28

No puedo creer que todavía estoy confundido acerca de esto, pero, de cualquier manera, finalmente podemos clavarlo:¿Debo llamar a Dispose() en objetos administrados?

Tengo una clase que anula OnPaint para hacer un dibujo. Para acelerar las cosas, creo los lápices, pinceles, etc. de antemano, en el constructor, para que OnPaint no necesite seguir creando y desechando.

Ahora, me aseguro de que siempre me deshago de esos objetos, pero tengo la sensación de que no es necesario porque, a pesar de que implementan IDisposable, son objetos administrados.

¿Es esto correcto?


Gracias por todas las respuestas, el problema ha sido clavado.
Me alegro de haber estado atento siempre usando 'usar' para no tener que pasar por todas las comprobaciones de código. Solo quería dejar claro que no estaba siendo un usuario sin sentido.

Como un aparte, tuve una situación extraña, recientemente, donde tuve que reemplazar un bloque de uso y llamar manualmente a deshacerse! Voy a cavar eso y crear una nueva pregunta.

+0

puede que proporcione los detalles (o enlace) para tener que reemplazar un bloque de 'utilizar' con una llamada explícita a disponer? Tengo curiosidad por ver qué estaba pasando allí. –

Respuesta

43

Es no correcto. Debe eliminar los objetos que implementan IDisposable. Es por eso que implementan IDisposable - para especificar el hecho de que envuelven (directa o indirectamente) recursos no administrados.

En este caso, el recurso no administrado es un identificador de GDI, y si no los elimina cuando ya ha finalizado, perderá esos identificadores. Ahora bien, estos objetos particulares tienen finalizadores que harán que se liberen los recursos cuando se active el GC, pero no hay forma de saber cuándo ocurrirá eso. Podría ser dentro de 10 segundos, podría ser dentro de 10 días; si su aplicación no genera suficiente presión de memoria para hacer que el GC entre y ejecute los finalizadores en esos pinceles/bolígrafos/fuentes/etc., puede terminar muriendo de hambre con el sistema operativo de los recursos de GDI antes de que el GC se dé cuenta de lo que está pasando.

Además, no tiene garantía de que cada contenedor no administrado realmente implemente un finalizador. .NET Framework es bastante consistente en el sentido de que las clases que implementan IDisposable lo implementan con el correct pattern, pero es completamente posible que otra clase tenga una implementación interrumpida que no incluye un finalizador y por lo tanto no se limpia correctamente a menos que se llame explícitamente Dispose. En general, el propósito de IDisposable es que no debe saber o preocuparse por los detalles de implementación específicos; más bien, si es desechable, entonces deséchelo, punto.

Moraleja de la historia: deseche siempre IDisposable objetos.Si su clase "posee" objetos que son IDisposable, entonces debería implementar IDisposable.

+0

Podría llamar al uso de finalizadores el patrón "ideal", pero hay muchos casos en que los objetos identificables ID no pueden implementar de manera útil los finalizadores, p. donde usan variables thread-static o suscripciones de eventos de objetos desconocidos. Los finalizadores se ejecutan en un contexto de subproceso diferente de otro código, que tendría dificultades para acceder a las variables estáticas de subprocesos asociadas con un IDisposable abandonado creado en otro subproceso. Si bien es posible diseñar eventos para ser desenganchados por un finalizador, la mayoría de los eventos no se pueden desenganchar de manera segura en dicho contexto. – supercat

+1

@supercat: Es un diseño muy peligroso que almacena recursos no administrados en el almacenamiento local de subprocesos. Es cierto que los finalizadores probablemente se ejecutarán en un hilo diferente, pero ¿cómo se puede garantizar que se llame a 'Dispose' del mismo hilo que causó la inicialización del recurso? Puedo pensar en varias situaciones en las que ese no sería el caso. Honestamente, no use ThreadStatic para ese tipo de cosas, mantenga los recursos críticos donde pueda rastrearlos. – Aaronaught

+0

Los elementos en el almacenamiento local de subprocesos son a menudo, * en sí mismos *, recursos no administrados; si el código que los crea cede el control sin borrarlos, pero el hilo persiste (quizás en el grupo de subprocesos), tales elementos pueden no limpiarse nunca. El uso de IDisposable para tales cosas les permite ser manejados con bloques "usando", que por su propia naturaleza manejarán el roscado correctamente. – supercat

9

Tienes que desecharlos.

El objeto administrado administra su memoria por sí mismos. Pero la memoria no es el único recurso que usa un objeto. Dispose() está destinado a liberar los otros recursos.

1

No, IDisposable es para objetos administrados que usan recursos no administrados. Como regla general, siempre debe desecharlos cuando haya terminado.

1

que realmente necesita para buscar la documentación de los pinceles, lápices, etc.

Si no están utilizando los recursos no administrados, es posible que no tenga que llamar a Dispose. Pero el patrón de uso/eliminación a veces se "utiliza incorrectamente". Como ejemplo, considere el marco ASP.NET MVC. Aquí puede escribir algo como:

using(Html.BeginForm(...)){ 
    ///HTML input fields etc. 
} 

Cuando Html.BeginForm(...) se llama, una etiqueta FORM se emitirá. Cuando termina el enunciado de utilización, se llamará al Dispose sobre el objeto devuelto desde el Html.BeginForm(...). Llamar al Dispose hace que se muestre la etiqueta FORM final. De esta forma, el compilador realmente aplicará el emparejamiento de etiquetas FORM, por lo que no olvidará la etiqueta de cierre.

2

¿Ha perfilado esto para ver si la creación de & ¿Desechar estos objetos realmente es un problema? Yo no creo que sea

Hace las cosas mucho más fáciles para usted y ciertamente menos propenso a errores simplemente siguiendo el patrón create-in-a-using-block.

Si desea crearlos una vez, también implemente IDisposable en su clase propietaria e itere el Dispose sobre sus propios objetos. No es necesario un destructor (finalizador).

No hay ningún costo para hacer esto en objetos que realmente no necesitan Deshacerse, pero hay un gran costo si olvida Eliminar en un objeto que lo necesita.

1

No, Pen s y Brush es son no objetos completamente gestionados.

Contienen un identificador a un recurso no administrado, es decir, el objeto GDI correspondiente en el sistema de gráficos subyacente. (No estoy seguro acerca de la terminología exacta aquí ...)

Si no los desecha, los mangos no se liberarán hasta que los objetos sean finalizados por el recolector de basura, y no hay garantía de que va a suceder pronto o en absoluto.

3

Escribí un componente de diagramación GDI + que usaba muchos lápices y pinceles. Los creé y los coloqué en el bloque de código que estaba haciendo el dibujo y el rendimiento nunca fue un problema. Mejor que tener un mango de larga duración en el SO en mi humilde opinión.

1

No, eso es incorrecto. Estoy de acuerdo con Aaronaught.

Además, Microsoft recomienda, en una transmisión web de mediados de 2003 que presentó Don Box, que cada desarrollador de .Net debería deshacerse de sus propios objetos, ya sea administrados o no, ya que esto mejora el rendimiento del código hasta en un 20%. Si se hace bien, puede ser una mejora sustancial en el rendimiento. Entonces, es una habilidad central que todo desarrollador .net necesita saber y usar.

5

Hay una marcada ironía en su enfoque. Al pre-crear los bolígrafos/pinceles, está exactamente creando el problema que Dispose() está tratando de resolver. Esos objetos GDI durarán más tiempo, como lo estarían cuando no llames a Dispose(). En realidad, es peor, estarán al menos hasta que se cierre el formulario.

Probablemente sean lo suficientemente extensas como para pasar a la generación n. ° 2. El recolector de basura no hace una colección gen # 2 muy a menudo, ahora es más importante para llamar a Dispose() en ellos. Para hacerlo, mueva el método Dispose() del formulario del archivo Designer.cs al archivo form.cs y agregue las llamadas a Dispose.

Pero, hazlo de la manera correcta. Las plumas y los pinceles son objetos muy baratos. Crételos cuando los necesite, en el evento Paint. Y use la instrucción using para que se eliminen de inmediato. Use la clase Cronómetro para asegurarse de que esto no causa ninguna desaceleración.

1

Otros han aludido a "utilizando" bloques de objetos GDI - aquí está un ejemplo de código:

using(var bm = new Bitmap()) 
using(var brush = new Brush()) 
{ 

    // code that uses the GDI objects goes here 
    ... 

} // objects are automatically disposed here, even if there's an exception 

Tenga en cuenta que puede tener tantos "por medio de" líneas como desee para un solo bloque de código.

Creo que esta es una buena y limpia forma de tratar objetos desechables.

0

Aunque ha preguntado sobre bolígrafos y pinceles, Font es una clase con algunos caprichos extraños. En particular, si se crea una fuente con el fin de establecer la propiedad Font de un control, uno sigue siendo responsable de eliminar esa fuente; la propiedad no se transfiere al control, pero esa responsabilidad se puede llevar a cabo desechando la fuente en en cualquier momento, incluso tan pronto como se cree la fuente, antes de asignarla al control. Parece que Font es una combinación de un objeto de información administrada y un recurso GDI no administrado, y para algunos fines solo se necesita el primero. Diseño extraño: la fuente debería haber sido de dos clases.

Cuestiones relacionadas