c# - ¿Puede la instrucción using ser reemplazada por llaves?
scope using-statement (7)
La sentencia using
es un azúcar sintáctico que llama a Dispose
sobre los objetos inicializados dentro de ()
, por lo que no puede simplemente reemplazarlo como lo hizo en su ejemplo.
Notará que los únicos objetos que puede usar dentro de una declaración using
son aquellos que implementan IDisposable
, lo que asegura que se puede IDisposable
Dispose
.
Como explica este artículo, el compilador transformará esto:
using (MyResource myRes = new MyResource())
{
myRes.DoSomething();
}
A esto:
MyResource myRes= new MyResource();
try
{
myRes.DoSomething();
}
finally
{
if (myRes!= null)
((IDisposable)myRes).Dispose();
}
Entonces, a menos que duplique esta estructura, no obtendrá el mismo comportamiento.
Además, reutilizar una variable de la manera que eres en tu ejemplo es una mala práctica. Alguien que lee el código puede pensar que está mirando la conexión 1 cuando de hecho está mirando a 2 o 3. Podría terminar siendo muy confuso y causar todo tipo de problemas en el futuro.
Uso la instrucción using para SqlConnection
. Es bueno para el rendimiento porque las fuerzas que llaman a Dispose () simplemente liberan la conexión al grupo antes.
Sin embargo, me di cuenta de que el objeto creado al usar no puede redefinirse. No puedo hacer esto
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
//...
connection = new SqlConnection(connectionString2);
//...
connection = new SqlConnection(connectionString3);
}
Me preguntaba si puedo reemplazar el uso y hacer algo como esto:
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
//...
connection = new SqlConnection(connectionString2);
//...
connection = new SqlConnection(connectionString3);
}
El SqlConnection
no será accesible después del último }
paréntesis. ¿Será el Dispose () llamado inmediatamente cuando el objeto sale del alcance?
No, el uso crea algunas construcciones de limpieza específicas que no obtendrás solo con los refuerzos.
Si usas Reflector y miras el IL, hay una llamada a Dispose added al final del bloque de uso para su objetivo:
L_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
L_000b: stloc.0
L_000c: nop
L_000d: nop
L_000e: leave.s L_0020
L_0010: ldloc.0
L_0011: ldnull
L_0012: ceq
L_0014: stloc.1
L_0015: ldloc.1
L_0016: brtrue.s L_001f
L_0018: ldloc.0
**L_0019: callvirt instance void [mscorlib]System.IDisposable::Dispose()**
L_001e: nop
L_001f: endfinally
Esto explica por qué no puede crear una nueva conexión dentro del bloque de uso y asignarlo a la variable; al hacerlo, la referencia original quedará colgando y sin oposición.
No, las cosas no se limpiarán automáticamente en tu segundo ejemplo (de hecho, con el código que tienes, dejarás varias conexiones abiertas).
No solo eso, sino que pierde la limpieza automática en caso de que se arrojen Excepciones dentro del bloque de using
. Recuerde que un bloque en using
se descompone en:
SqlConnection connection = new SqlConnection(connectionString);
try
{
connection.Open();
// Do work
}
finally
{
connection.Dispose();
}
Si realmente está usando conexiones diferentes y cada conexión está Dispuesta de al final del bloque, usaría varios bloques de uso:
using(SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Do Work
}
// First connection is disposed
using(SqlConnection connection = new SqlConnection(connectionString2))
{
// Do More Work
}
// Second connection is disposed
using(SqlConnection connection = new SqlConnection(connectionString3))
{
// Do More Work
}
// Last connection is dipsosed
No, no puedes hacer esto. Al menos en C #.
Pero puede crear diferentes objetos desechables dentro de una declaración using:
using (SqlConnection connection1 = new SqlConnection(connectionString1),
connection2 = new SqlConnection(connectionString2),
connection3 = new SqlConnection(connectionString3))
{
connection1.Open();
//...
connection2.Open();
//...
connection3.Open();
}
C ++ / CLI puede usar sus clases desechables en una facción similar a una pila:
void MyClass::Foo()
{
{
SqlConnection connection(connectionString);
//connection still allocated on the managed heap
connection.Open();
...
//connection will be automatically disposed
//when the object gets out of scope
}
{
SqlConnection connection(connectionString2);
connection2.Open();
...
}
}
No, no se llamará después de la llave de cierre. Debe llamarlo manualmente o usar una instrucción de using
.
Si no ejecuta el desecho, se ejecutará cuando se llame a la finalización. Y aquí tienes 2 problemas
- Ejecutar un método Finalize es costoso para el rendimiento. Si su método Dispose ya ha hecho el trabajo para limpiar el objeto, entonces no es necesario que el recolector de basura llame al método Finalize del objeto (si está bien implementado, un método Dispose debería llamar al método SuppressFinalize para el objeto que está desechando ) (MSDN)
- No se controla el momento en el que el finalizador se llama automáticamente y no se puede ejecutar (debido a un bloqueo, por ejemplo).
También pensé esto al principio ... pero aparentemente cuando termina el bloque de uso, se llama a .Dispose()
en tu objeto, que es diferente de dejar que el objeto salga del alcance. Debido a que C # es un lenguaje recogido de basura, podría tomar tiempo antes de que su objeto se haya limpiado realmente. El bloque de using
garantiza que se elimine de inmediato, independientemente de las excepciones.
using(foo)
{
// stuff
}
... es un poco de azúcar que se traduce en:
try
{
// stuff
}
finally
{
foo.Dispose()
}
Mientras:
{
// stuff
}
... no se traduce en nada. Es solo:
{
// stuff
}
:RE
Editar: no destruya el formato cuando edite :(