que - implementar idisposable c#
Uso de instrucción vs. IDisposable.Dispose() (9)
Tengo entendido que la instrucción using
en .NET llama a un IDisposable
Dispose()
del objeto IDisposable
una vez que el código sale del bloque.
¿La instrucción using
hace algo más? De lo contrario, parecería que los siguientes dos ejemplos de código logran exactamente lo mismo:
Using Con as New Connection()
Con.Open()
''do whatever ''
End Using
Dim Con as New Connection()
Con.Open()
''do whatever ''
Con.Dispose()
Daré la mejor respuesta a quien confirme que estoy en lo cierto o que estoy equivocado y explico por qué. Tenga en cuenta que soy consciente de que ciertas clases pueden hacer cosas diferentes en sus métodos Dispose()
. Esta pregunta se trata de si la instrucción de using
logra el mismo resultado que llamar al método Dispose()
un objeto.
Como dijo Brian Warshaw here , es simplemente una implementación de try
y finally
bloquear para asegurarse de que el objeto se elimine. Agregando a su respuesta, el using
bloque también asegura que el objeto se elimine incluso si regresa dentro utilizando el alcance .
Una vez tuve curiosidad sobre esto y lo probé usando el siguiente enfoque:
Clase de prueba IDisposable personalizada y Principal
private class DisposableTest : IDisposable
{
public string Name { get; set; }
public void Dispose() { Console.WriteLine("{0}.Dispose() is called !", Name); }
}
public static void Main(string[] args)
{
try
{
UsingReturnTest();
UsingExceptionTest();
}
catch { }
try
{
DisposeReturnTest();
DisposeExceptionTest();
}
catch { }
DisposeExtraTest();
Console.ReadLine();
}
Implementación de casos de prueba
private static string UsingReturnTest()
{
using (DisposableTest usingReturn = new DisposableTest() { Name = "UsingReturn" })
{
return usingReturn.Name;
}
}
private static void UsingExceptionTest()
{
using (DisposableTest usingException = new DisposableTest() { Name = "UsingException" })
{
int x = int.Parse("NaN");
}
}
private static string DisposeReturnTest()
{
DisposableTest disposeReturn = new DisposableTest() { Name = "DisposeReturn" };
return disposeReturn.Name;
disposeReturn.Dispose(); // # IDE Warning; Unreachable code detected
}
private static void DisposeExceptionTest()
{
DisposableTest disposeException = new DisposableTest() { Name = "DisposeException" };
int x = int.Parse("NaN");
disposeException.Dispose();
}
private static void DisposeExtraTest()
{
DisposableTest disposeExtra = null;
try
{
disposeExtra = new DisposableTest() { Name = "DisposeExtra" };
return;
}
catch { }
finally
{
if (disposeExtra != null) { disposeExtra.Dispose(); }
}
}
Y la salida es:
- Usando Return.Dispose () es llamado!
- UsingException.Dispose () se llama!
- ¡Se llama a DisposeExtra.Dispose ()!
El bloque que usa se asegura de que se Dispose()
si se lanza una excepción.
Tu segunda muestra no hace eso.
Si Con.Open()
arrojó una excepción, en el primer caso se le garantiza que se llama a Con.Dispose()
. En el segundo caso, la excepción se propaga y Con.Dispose()
no se llamará.
El uso envuelve el bloque incluido en un try / finally que llama a Dispose en el bloque finally. Esto asegura que se llamará a Dispose incluso si se produce una excepción.
Debe usarlo en casi todos los casos, por razones de seguridad
La diferencia entre los dos es que, si se lanza una excepción
Con.Open()
''do whatever
Con.Dispose
no se llamará.
No estoy al tanto de la sintaxis de VB, pero en C #, el código equivalente sería
try
{
con = new Connection();
// Do whatever
}
finally
{
if (con != null) con.Dispose();
}
La instrucción using
garantiza que el objeto se elimina en caso de que se genere una excepción. Es el equivalente a llamar a disponer en un bloque final.
Si la memoria le sirve, usar es una garantía de que un objeto se desecha independientemente de cómo el bloque de código que lo rodea lo abandona. Lo hace rodeando el bloque en un intento ... finalmente bloqueando, y comprobando la nulidad de la variable utilizada , y luego deshaciéndose de ella si no es nula. Si se lanzó una excepción, se permite que suba la pila. Aparte de eso, todo lo que hace es garantizar la eliminación de objetos desechables no nulos.
try
{
var myDisposable = new DisposableObject();
myDisposable.DoSomething();
}
finally
{
if (myDisposable != null)
((IDisposable)myDisposable).Dispose();
}
Una instrucción de using
es más clara y concisa que un try...finally{Dispose()}
constructo, y debe usarse en casi todos los casos donde uno no quiere permitir que un bloque salga sin que se Dispose
. Las únicas situaciones comunes donde la eliminación "manual" sería mejor sería cuando:
- Un método llama a un método de fábrica que devuelve algo que puede o no implementar `IDisposable`, pero que debería ser` Dispose`d si lo hace (un escenario que ocurre con `IEnumerable.GetEnumerator ()` no genérico). Interfaces de fábrica bien diseñadas deberían devolver un tipo que implemente `IDisposable` (quizás con una implementación do-nothing, como suele ser el caso de` IEnumerator`) o, de lo contrario, especifique que los llamadores no deben `Dispose` el objeto devuelto. Desafortunadamente, algunas interfaces como `IEnumerable` no genérico no satisfacen ningún criterio. Tenga en cuenta que uno no puede usar `using` en tales casos, ya que solo funciona con ubicaciones de almacenamiento cuyo tipo declarado implementa` IDisposable`.
- Se espera que el objeto `IDisposable` viva incluso después de salir del bloque (como suele ser el caso cuando se establece un campo` IDisposable`, o se devuelve un `IDisposable` de un método de fábrica).
Tenga en cuenta que al devolver un IDisposable
de un método de fábrica, uno debe usar algo como lo siguiente:
bool ok = false; DisposableClass myThing; try { myThing = new DisposableClass(); ... ok = true; return myThing; } finally { if (!ok) { if (myThing != null) myThing.Dispose(); } }
para garantizar que myThing
obtendrá Dispose
d si no se devuelve. Desearía que hubiera una forma de emplear using
junto con algún método de "cancelar eliminación", pero eso no existe.
using
es básicamente el equivalente de:
try
{
// code
}
finally
{
obj.Dispose();
}
Por lo tanto, también tiene la ventaja de invocar a Dispose()
incluso si se lanza una excepción no controlada al código dentro del bloque.
//preceeding code
using (con = new Connection()) {
con.Open()
//do whatever
}
//following code
es equivalente a lo siguiente (tenga en cuenta el alcance limitado de estafa):
//preceeding code
{
var con = new Connection();
try {
con.Open()
//do whatever
} finally {
if (con != null) con.Dispose();
}
}
//following code
Esto se describe aquí: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
La instrucción using garantiza que se invoque Dispose incluso si se produce una excepción mientras se están llamando métodos al objeto. Puede lograr el mismo resultado colocando el objeto dentro de un bloque try y luego llamando a Dispose en un bloque finally; de hecho, así es como el compilador traduce la sentencia using .