remarks cref c# .net dispose idisposable

cref - remarks c#



¿Por qué debería Dispose() ser no virtual? (8)

Soy nuevo en C #, así que disculpas si esta es una pregunta obvia.

En el ejemplo de MSDN Dispose , el método de Dispose que definen no es virtual. ¿Porqué es eso? Me parece extraño: esperaría que una clase secundaria de un IDisposable que tuviera sus propios recursos no gestionados simplemente anularía a Disose y call base.Dispose () al final de su propio método.

¡Gracias!


Aunque los métodos en una interfaz no son "virtuales" en el sentido habitual, todavía pueden implementarse en las clases que los heredan. Aparentemente, esto es una comodidad integrada en el lenguaje C #, que permite la creación de métodos de interfaz sin requerir la palabra clave virtual , y la implementación de métodos sin la palabra clave de override .

En consecuencia, aunque la interfaz IDisposable contiene un método Dispose() , no tiene la palabra clave virtual delante de ella, ni tiene que usar la palabra clave override en la clase heredada para implementarla.

El patrón habitual de Dispose es implementar Dispose en su propia clase, y luego llamar a Dispose en la clase base para que pueda liberar los recursos que posee, y así sucesivamente.

El método Dispose de un tipo debería liberar todos los recursos que posee. También debe liberar todos los recursos que pertenecen a sus tipos básicos llamando al método Dispose de su tipo principal. El método Dispose del tipo principal debe liberar todos los recursos que posee y, a su vez, llamar al método Dispose de su tipo principal, propagando este patrón a través de la jerarquía de los tipos base.

http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx


El método Dispose no debe ser virtual porque no es un punto de extensión para que el patrón implemente desechable. Eso significa que la clase desechable base en una jerarquía creará la política de nivel superior (el algoritmo) para disponer y delegará los detalles al otro método ( Dispose(bool) ). Esta política de nivel superior es estable y no debe ser anulada por clases secundarias. Si permite que las clases secundarias lo anulen, es posible que no llamen a todas las partes necesarias del algoritmo, lo que podría dejar el objeto en un estado incoherente.

Esto es similar al patrón de método de plantilla , en el que un método de alto nivel implementa un esquema de algoritmo y delega los detalles a otros métodos reemplazables.

Como nota al margen, prefiero another política de alto nivel para este patrón en particular (que aún utiliza un Dispose no virtual).


El uso típico es que Dispose () está sobrecargado, con un método público, no virtual, Dispose (), y un virtual, protegido Dispose (bool). El método público Dispose () llama a Dispose (verdadero), y las subclases pueden usar este método virtual protegido para liberar sus propios recursos, y llamar a la base. Disposición (verdadero) para las clases primarias.

Si la clase que posee el método público Dispose () también implementa un finalizador, entonces el finalizador llama a Dispose (falso), lo que indica que el método protegido de Dispose (bool) fue llamado durante la recolección de basura.

Si hay un finalizador, entonces el método público Dispose () también es responsable de llamar a GC.SuppressFinalize () para asegurarse de que el finalizador ya no esté activo y nunca se llamará. Esto permite que el recolector de basura trate la clase normalmente. Las clases con finalizadores activos generalmente se recopilan solo como último recurso, después de la limpieza de gen0, gen1 y gen2.


Esto ciertamente no es obvio. Este patrón fue especialmente elegido porque funciona bien en los siguientes escenarios:

  • Clases que no tienen finalizador.
  • Clases que sí tienen un finalizador.
  • Clases que pueden ser heredadas de.

Si bien un método virtual Dispose() funcionará en el escenario donde las clases no necesitan finalización, no funciona bien en el escenario donde sí necesita finalización, porque esos tipos a menudo necesitan dos tipos de limpieza. A saber: limpieza administrada y limpieza no administrada. Por esta razón, el método Dispose(bool) se introdujo en el patrón. Evita la duplicación del código de limpieza (este punto falta en las otras respuestas), porque el método Dispose() normalmente limpiará los recursos administrados y no administrados, mientras que el finalizador solo puede limpiar los recursos no administrados.


La razón por la que el método Dispose () de la muestra no es virtual es porque se encargan de todo el proceso en ese ejemplo, y dejan las subclases con el método virtual Dispose (bool disposing) para anular. Notará que en el ejemplo, almacena un campo booleano para garantizar que la lógica de Disposición no se invoque dos veces (potencialmente una vez desde IDisposable, y una vez desde el destructor). Las subclases que anulan el método virtual proporcionado no tienen que preocuparse por este matiz. Es por esto que el método principal de Dispose en el ejemplo no es virtual.


Las llamadas a través de una interfaz son siempre virtuales, independientemente de si una llamada "normal" sería directa o virtual. Si el método que realmente realiza el trabajo de eliminación no es virtual, excepto cuando se llama a través de la interfaz, entonces, cada vez que la clase desee disponer, tendrá que asegurarse de enviar su propia referencia a iDisposable y llamar a eso.

En el código de la plantilla, se espera que la función de Dispose no virtual sea siempre la misma que la principal y la secundaria [simplemente llamando a Dispose (Verdadero)], por lo que nunca hay necesidad de anularla. Todo el trabajo se realiza en el Dispose virtual (Booleano).

Francamente, creo que usar el patrón de Disposición es un poco tonto en los casos en que no hay razón para esperar que las clases descendientes tengan directamente recursos no administrados. En los primeros días de .net, a menudo era necesario que las clases tuvieran directamente recursos no administrados, pero hoy en la mayoría de las situaciones no veo ninguna pérdida por la simple implementación de Dispose () directamente. Si una futura clase descendiente necesita usar recursos no administrados, puede y típicamente debe envolver esos recursos en sus propios objetos Finalizables.

Por otro lado, para ciertos tipos de métodos, puede haber ventajas en tener un método de clase base no virtual cuyo trabajo es encadenar a un método virtual protegido, y que el método virtual se llame Dispose(bool) es realmente peor que VirtDispose() incluso si el argumento proporcionado es bastante inútil. En algunas situaciones, por ejemplo, puede ser necesario que todas las operaciones en un objeto estén protegidas por un bloqueo que es propiedad del objeto de clase base. Hacer que Dispose adquiera el bloqueo de la clase base no virtual antes de llamar al método virtual liberará a todas las clases base de tener que preocuparse por el bloqueo.


Si la clase base tiene recursos que deben limpiarse en el momento de Dispose() , el hecho de tener un método virtual de Dispose que es invalidado por una clase hereditaria evita que esos recursos se liberen, a menos que la clase hereditaria llame específicamente al método Dispose la base. Una mejor manera de implementarlo sería hacer que cada clase derivada implementara IDisposable .


Tengo una explicación bastante detallada del patrón de disposición here . Esencialmente, proporciona un método protected para anular que es más robusto para los recursos no administrados.