method - C#Cómo implementar el método Dispose
recursos no administrados c# (3)
Implemento IDisposable
class ConnectionConfiguration:IDisposable
{
private static volatile IConnection _rbMqconnection;
private static readonly object ConnectionLock = new object();
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
return;
}
if (_rbMqconnection == null)
{
return;
}
lock (ConnectionLock)
{
if (_rbMqconnection == null)
{
return;
}
_rbMqconnection?.Dispose();//double check
_rbMqconnection = null;
}
}
}
Necesito un consejo sobre la implementación del método Dispose
.
En nuestra aplicación el usuario diseña su propia interfaz de usuario. Tengo una ventana de vista previa que muestra cómo se verá la interfaz de usuario. Todos los objetos dibujados en esta IU derivan finalmente de una clase base común ScreenObject. Mi administrador de vista previa contiene una única referencia de objeto a un ScreenGrid que es el objeto de cuadrícula para el área de vista previa completa.
Pregunta 1
Algunas de mis clases de pantalla derivadas conservan recursos no administrados, como una conexión de base de datos, una imagen de mapa de bits y un control WebBrowser
. Estas clases necesitan disponer de estos objetos. ScreenObject
un método Dispose
virtual en la clase base de ScreenObject
base y luego implementé un método Dispose
reemplazo en cada una de las clases derivadas que se aferran a recursos no administrados. Sin embargo, en este momento acabo de crear un método llamado Dispose
, no estoy implementando IDisposable
. ¿Debo implementar IDisposable
? Si es así, ¿cómo lo implemento?
- Solo en las clases derivadas que tienen recursos no gestionados
- La clase base y las clases derivadas que tienen recursos no administrados O
- La clase base y todas las clases derivadas, incluidas aquellas que no tienen recursos no administrados
¿Es incorrecto colocar un método virtual de Dispose
en una clase base que no tiene recursos no administrados para que pueda aprovechar el polimorfismo?
Pregunta 2
Al leer sobre el método Dispose
y la interfaz IDisposable
, Microsoft declara que el objeto de disposición solo debe llamar al método Dispose
para su principal. El padre lo llamará por su padre y así sucesivamente. A mí esto me parece al revés. Es posible que desee disponer de un niño pero mantener a su padre cerca.
Pensaría que debería ser al revés, un objeto que se está eliminando debería disponer de sus hijos. Los niños deben disponer de sus hijos y así sucesivamente.
¿Estoy equivocado aquí o me estoy perdiendo algo?
Pregunta 1:
Según los tipos de objetos que enumera (es decir, Base de datos, WebBrowser, Mapa de bits, etc.), estos son solo recursos administrados en lo que respecta a .NET. Por lo tanto, debe implementar IDisposable
en cualquier clase que tenga tipos desechables como miembros. Si son instancias declaradas localmente, solo debes llamar ''using ()'' en ellas. Si bien estas instancias que mencionas tienen recursos no administrados, .NET las extrae de ti a través de los tipos que estás utilizando. Como solo está utilizando tipos administrados, debe implementar IDisposable
pero sin un finalizador . Solo necesita implementar un finalizador si realmente tiene recursos no administrados como miembros de la clase.
Pregunta 2:
Parece que está confundiendo herencia (es a) con agregación / contención (tiene a). Por ejemplo, si "Contenedor" contiene un recurso desechable como miembro de la clase, se denomina agregación / contención. Por lo tanto, llamar a base.Dispose()
en la implementación IDisposable
de Container no tiene nada que ver con la eliminación del recurso disponible dentro de Container. Debe recordar que si una clase se deriva de Container, diga "DerivedContainer", que es una instancia de Container aunque con miembros y / o funcionalidad adicionales. Por lo tanto, cualquier instancia de "DerivedContainer" tiene todos los miembros que tiene su clase base "Container". Si nunca llamó a base.Dispose()
, el recurso desechable en "Container" no se lanzaría correctamente (en realidad lo haría el GC, pero es una mala práctica por muchas razones simplemente ''dejar que .NET se encargue de ello'') - Consulte mi respuesta publicada en ¿Es una mala práctica depender del recolector de basura automatizado .NET? .
Si no llamó a la clase base Dispose()
, terminaría con un objeto parcialmente dispuesto (dispuesto en la clase derivada pero no en la clase base), un escenario muy malo. Por eso es muy importante llamar a la clase base Dispose()
.
Tengo un patrón de mejores prácticas que he desarrollado (con mucha experiencia y depuración de volcados de memoria) escrito en mi blog como ejemplo. Muestra cómo implementar IDisposable
en una clase base, así como una clase derivada:
http://dave-black.blogspot.com/2011/03/how-do-you-properly-implement.html
Pregunta 1: Implemente IDisposable
también, usando el siguiente patrón:
public class MyClass : IDisposable
{
bool disposed;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
}
//dispose unmanaged resources
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Pregunta 2: Lo que quiere decir Microsoft es que las llamadas de una clase derivada se eliminan en su clase primaria. El propietario de la instancia solo llama a Dispose en el tipo más derivado.
Un ejemplo (acortado):
class Parent : IDisposable
{
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
}
//dispose unmanaged resources
disposed = true;
}
}
class Child : Parent, IDisposable
{
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//dispose managed resources
}
base.Dispose(disposing);
}
//dispose unmanaged resources
disposed = true;
}
}
class Owner:IDisposable
{
Child child = new Child();
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if(child!=null)
{
child.Dispose();
}
}
}
//dispose unmanaged ressources
disposed = true;
}
}
El propietario solo llama a Dispose
on the Child, pero no a Parent. El niño es responsable de llamar a Dispose
sobre el padre.