method - Cómo verificar si el objeto se ha eliminado en C#
recursos no administrados c# (4)
La solución confiable es atrapar la ObjectDisposedException.
La solución para escribir su implementación anulada del método Dispose no funciona, ya que existe una condición de carrera entre el método Dispose que llama al subproceso y el que accede al objeto: después de haber verificado la propiedad hipotética IsDisposed, el objeto podría ser realmente eliminado , lanzando la excepción de todos modos.
Otro enfoque podría ser exponer un evento hipotético Desechado (como this ), que se utiliza para notificar sobre el objeto de disposición a cada objeto interesado, pero esto podría ser difícil de planificar dependiendo del diseño del software.
Posible duplicado:
¿Cómo se puede decir si se elimina una referencia de objeto IDisponible?
¿Existe un método para verificar si el objeto se ha eliminado de manera diferente?
try
{
myObj.CallRandomMethod();
} catch (ObjectDisposedException e)
{
// now I know object has been disposed
}
En mi caso, estoy usando la clase TcpClient
que tiene el método Close()
que elimina el objeto y esto puede suceder en un fragmento de código del que no tengo control. En este caso, me gustaría tener una mejor solución que la captura de excepciones.
Las mejores prácticas dicen implementarlo por su cuenta utilizando el campo booleano local: http://www.niedermann.dk/2009/06/18/BestPracticeDisposePatternC.aspx
Si no está seguro de si el objeto se ha eliminado o no, debe llamar al método Dispose
en lugar de a métodos como Close
. Si bien el marco no garantiza que el método Dispose se ejecute sin excepciones, incluso si el objeto se había eliminado previamente, es un patrón común y, según mi conocimiento, se implementó en todos los objetos desechables del marco.
El patrón típico para Dispose
, según Microsoft :
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
// If you need thread safety, use a lock around these
// operations, as well as in your methods that use the resource.
if (!_disposed)
{
if (disposing) {
if (_resource != null)
_resource.Dispose();
Console.WriteLine("Object disposed.");
}
// Indicate that the instance has been disposed.
_resource = null;
_disposed = true;
}
}
Observe el cheque en _disposed
. Si tuviera que llamar a un método Dispose
que implementa este patrón, podría llamar a Dispose tantas veces como quisiera sin golpear las excepciones.
Una buena forma es derivar de TcpClient y anular el método Disposing (bool):
class MyClient : TcpClient {
public bool IsDead { get; set; }
protected override void Dispose(bool disposing) {
IsDead = true;
base.Dispose(disposing);
}
}
Lo cual no funcionará si el otro código creó la instancia. Entonces tendrás que hacer algo desesperado como usar Reflection para obtener el valor del miembro privado m_CleanedUp. O atrapar la excepción.
Francamente, no es probable que esto tenga un buen final. Realmente querías escribir en el puerto TCP. Pero no lo harás, ese código de buggy que no puedes controlar ahora está en control de tu código. Has aumentado el impacto del error. Hablar con el propietario de ese código y resolver algo es, con mucho, la mejor solución.
EDITAR: Un ejemplo de reflexión:
using System.Reflection;
public static bool SocketIsDisposed(Socket s)
{
BindingFlags bfIsDisposed = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty;
// Retrieve a FieldInfo instance corresponding to the field
PropertyInfo field = s.GetType().GetProperty("CleanedUp", bfIsDisposed);
// Retrieve the value of the field, and cast as necessary
return (bool)field.GetValue(s, null);
}