method interfaz implement how example correctly .net dispose idisposable

.net - interfaz - idisposable xamarin



¿El Garbage Collector llamará a IDisposable.Dispose para mí? (9)

El .NET IDisposable Pattern implica que si escribe un finalizador e implementa IDisposable, su finalizador necesita llamar explícitamente a Dispose. Esto es lógico, y es lo que siempre he hecho en las raras situaciones en las que se justifica un finalizador.

Sin embargo, ¿qué sucede si solo hago esto?

class Foo : IDisposable { public void Dispose(){ CloseSomeHandle(); } }

y no implemente un finalizador, ni nada. ¿El marco llamará el método Dispose para mí?

Sí, me doy cuenta de que esto suena tonto, y toda la lógica implica que no lo hará, pero siempre he tenido dos cosas en la parte posterior de mi cabeza que me han dejado inseguro.

  1. Alguien hace unos años me dijo una vez que de hecho haría esto, y que esa persona tenía un historial muy sólido de "conocer sus cosas".

  2. El compilador / framework hace otras cosas ''mágicas'' dependiendo de qué interfaces implemente (por ejemplo: foreach, métodos de extensión, serialización basada en atributos, etc.), así que tiene sentido que esto también sea ''mágico''.

Si bien he leído muchas cosas al respecto, y ha habido muchas cosas implícitas, nunca he podido encontrar una respuesta definitiva de Sí o No a esta pregunta.


.Net Garbage Collector llama al método Object.Finalize de un objeto en la recolección de elementos no utilizados. Por defecto, esto no hace nada y debe ser invalidado si desea liberar recursos adicionales.

Dispose NO se llama automáticamente y debe invocarse explícitamente si se van a liberar recursos, como dentro de un bloque ''using'' o ''try finally''

ver http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx para más información


El GC no llamará a disponer. Puede llamar a su finalizador, pero incluso esto no está garantizado en todas las circunstancias.

Vea este article para una discusión sobre la mejor manera de manejar esto.


El patrón IDisposable fue creado principalmente para ser llamado por el desarrollador, si tiene un objeto que implementa un ID, el desarrollador debe implementar la palabra clave using en el contexto del objeto o llamar directamente al método Dispose.

El fail safe para el patrón es implementar el finalizador llamando al método Dispose (). Si no lo hace, puede crear algunas pérdidas de memoria, es decir: si crea algún contenedor COM y nunca llama al System.Runtime.Interop.Marshall.ReleaseComObject (comObject) (que se colocaría en el método Dispose).

No hay ninguna magia en el clr para llamar a los métodos de Disposición de forma automática que no sean objetos de seguimiento que contienen finalizadores y almacenarlos en la tabla de Finalizer por el GC y llamarlos cuando el GC active alguna heurística de limpieza.


La documentación sobre IDisposable brinda una explicación bastante clara y detallada del comportamiento, así como un código de ejemplo. El GC NO llamará al método Dispose() en la interfaz, pero llamará al finalizador de su objeto.


No en el caso que describa, pero el GC llamará al Finalizador por usted, si tiene uno.

SIN EMBARGO. La próxima recolección de elementos no utilizados, en lugar de recopilarse, el objeto irá a la finalización, se recopilará todo, luego se llamará al finalizador. La próxima colección después de eso será liberada.

Dependiendo de la presión de memoria de su aplicación, es posible que no tenga un gc para la generación de ese objeto por un tiempo. Por lo tanto, en el caso de, por ejemplo, una secuencia de archivos o una conexión de base de datos, es posible que tenga que esperar un tiempo para que el recurso no administrado se libere en la llamada al finalizador durante un tiempo, lo que ocasiona algunos problemas.


No lo creo. Usted tiene control sobre cuándo se llama Dispose, lo que significa que, en teoría, podría escribir un código de eliminación que haga suposiciones sobre (por ejemplo) la existencia de otros objetos. No tiene control sobre cuándo se llama al finalizador, por lo que sería dudoso que el finalizador llame automáticamente a Dispose en su nombre.

EDIT: fui y probé, solo para asegurarme de que:

class Program { static void Main(string[] args) { Fred f = new Fred(); f = null; GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("Fred''s gone, and he''s not coming back..."); Console.ReadLine(); } } class Fred : IDisposable { ~Fred() { Console.WriteLine("Being finalized"); } void IDisposable.Dispose() { Console.WriteLine("Being Disposed"); } }


No, no es llamado.

Pero esto hace que sea fácil no olvidarse de disponer de sus objetos. Simplemente use la palabra clave using .

Hice la siguiente prueba para esto:

class Program { static void Main(string[] args) { Foo foo = new Foo(); foo = null; Console.WriteLine("foo is null"); GC.Collect(); Console.WriteLine("GC Called"); Console.ReadLine(); } } class Foo : IDisposable { public void Dispose() { Console.WriteLine("Disposed!"); }


Quiero enfatizar el punto de Brian en su comentario, porque es importante.

Los finalizadores no son destructores deterministas como en C ++. Como han señalado otros, no hay garantía de cuándo será llamado, y de hecho si tienes suficiente memoria, si alguna vez se llamará.

Pero lo malo de los finalizadores es que, como dijo Brian, hace que su objeto sobreviva a una recolección de basura. Esto puede ser malo. ¿Por qué?

Como puede o no puede saber, el GC se divide en generaciones: Gen 0, 1 y 2, más el Gran Montón de Objetos. Dividir es un término flexible: obtienes un bloque de memoria, pero hay indicadores de dónde comienzan y finalizan los objetos Gen 0.

El proceso de pensamiento es que probablemente uses muchos objetos que serán de corta duración. Entonces, debe ser fácil y rápido para el GC: objetos Gen 0. Entonces, cuando hay presión de memoria, lo primero que hace es una colección Gen 0.

Ahora, si eso no resuelve suficiente presión, vuelve y hace un barrido de Gen 1 (rehaciendo Gen 0), y luego, si aún no es suficiente, hace un barrido Gen 2 (rehacer Gen 1 y Gen 0). Por lo tanto, la limpieza de objetos de larga vida puede llevar un tiempo y ser bastante costosa (ya que los hilos pueden suspenderse durante la operación).

Esto significa que si haces algo como esto:

~MyClass() { }

Su objeto, pase lo que pase, vivirá en la Generación 2. Esto se debe a que el GC no tiene forma de llamar al finalizador durante la recolección de basura. Por lo tanto, los objetos que se tienen que finalizar se mueven a una cola especial para ser eliminados por un hilo diferente (el hilo del finalizador, que si mata hace que sucedan todo tipo de cosas malas). Esto significa que sus objetos permanecen por más tiempo y potencialmente fuerzan más recolecciones de basura.

Por lo tanto, todo eso es solo para llevar a casa el punto en el que desea usar IDisposable para limpiar los recursos siempre que sea posible y tratar seriamente de encontrar formas de utilizar el finalizador. Está en los mejores intereses de su aplicación.


Ya hay muchas buenas discusiones aquí, y llegué un poco tarde a la fiesta, pero yo quería agregar algunos puntos.

  • El recolector de basura nunca ejecutará directamente un método de eliminación para ti.
  • El GC ejecutará los finalizadores cuando así lo desee.
  • Un patrón común que se utiliza para los objetos que tienen un finalizador es hacer que lo denomine un método que por convención se define como Dispose (bool disposing) que pasa falso para indicar que la llamada se realizó debido a la finalización en lugar de una llamada Dispose explícita.
  • Esto se debe a que no es seguro hacer suposiciones sobre otros objetos gestionados mientras se finaliza un objeto (es posible que ya se hayan finalizado).

class SomeObject : IDisposable { IntPtr _SomeNativeHandle; FileStream _SomeFileStream; // Something useful here ~ SomeObject() { Dispose(false); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if(disposing) { GC.SuppressFinalize(this); //Because the object was explicitly disposed, there will be no need to //run the finalizer. Suppressing it reduces pressure on the GC //The managed reference to an IDisposable is disposed only if the _SomeFileStream.Dispose(); } //Regardless, clean up the native handle ourselves. Because it is simple a member // of the current instance, the GC can''t have done anything to it, // and this is the onlyplace to safely clean up if(IntPtr.Zero != _SomeNativeHandle) { NativeMethods.CloseHandle(_SomeNativeHandle); _SomeNativeHandle = IntPtr.Zero; } } }

Esa es la versión simple, pero hay muchos matices que pueden hacerte tropezar con este patrón.

  • El contrato para IDisposable.Dispose indica que debe ser seguro llamar varias veces (llamar a Dispose en un objeto que ya se eliminó no debe hacer nada)
  • Puede ser muy complicado administrar adecuadamente una jerarquía de herencia de objetos desechables, especialmente si las diferentes capas presentan nuevos recursos desechables y no administrados. En el patrón anterior, Dispose (bool) es virtual para permitir su anulación para que se pueda administrar, pero creo que es propenso a errores.

En mi opinión, es mucho mejor evitar por completo cualquier tipo que contenga directamente referencias desechables y recursos nativos que puedan requerir una finalización. SafeHandles proporciona una forma muy limpia de hacerlo encapsulando recursos nativos en desechables que internamente proporcionan su propia finalización (junto con una serie de otros beneficios, como eliminar la ventana durante P / Invoke, donde un identificador nativo podría perderse debido a una excepción asincrónica) .

Simplemente definir un SafeHandle hace que esto sea trivial:

private class SomeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public SomeSafeHandle() : base(true) { } protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(handle); } }

Le permite simplificar el tipo que contiene a:

class SomeObject : IDisposable { SomeSafeHandle _SomeSafeHandle; FileStream _SomeFileStream; // Something useful here public virtual void Dispose() { _SomeSafeHandle.Dispose(); _SomeFileStream.Dispose(); } }