2009-04-03 15 views
7

Soy algo nuevo en C#, más acostumbrado a los lenguajes de scripting. Me gusta la idea de "usar", crea una instancia de un objeto, y luego opera dentro de su alcance todo el tiempo que lo necesita, luego deja que se deshaga de sí mismo cuando ha cumplido su propósito.¿Cómo sé cuál es el mejor lugar para usar 'usar'?

Pero, no es natural para mí. Cuando las personas me muestran ejemplos que lo usan, lo reconozco como una buena herramienta para el trabajo, pero nunca se me ocurre resolver problemas con él en mi propia programación.

¿Cómo puedo reconocer un buen lugar para utilizar using y cómo lo uso en conjunción con bloques try-catch. ¿Van dentro del bloque, o generalmente quieren adjuntar una declaración de uso dentro de un bloque de prueba?

Respuesta

9

rara vez escribir bloques try/catch - la mayoría de las excepciones son arrojados hasta (casi) la parte superior de la pila. Si yo hago necesitan un bloque try/catch, no estoy seguro de que estoy particularmente consistente entre ponerlo dentro de la declaración using vs exterior. Realmente depende de si desea que el recurso se elimine antes o después de ejecutar su código de manejo de excepciones.

Si preguntas acerca cuando se debe escribir using declaraciones - en cualquier momento que "propia" un objeto que implementa IDisposable (ya sea directa o indirectamente a través de la herencia) y controlar su tiempo de vida. Por lo general, es un objeto que utiliza un recurso no administrado como un identificador de archivo o una conexión de red. No siempre es muy obvio, pero aprendes a través de la experiencia. Casi cualquier cosa que hacer con IO será desechable, y los controladores de Windows (para fuentes, etc.) son similares.

+0

Para aclarar objetos que implementan IDisposable directamente o por herencia (ya que danieltalsky es "algo nuevo para C#"). –

+0

Gracias Jonathan - editado apropiadamente. –

+0

Bueno, por "algo nuevo para C#" quiero decir, "he estado escribiendo código comercial en C# durante menos de 8 meses". Yo, por ejemplo, ya estaba familiarizado con la conexión de "usar" a "IDisposable" que menciono en la pregunta. Era la parte de "control de por vida" que me faltaba. – danieltalsky

18

using sólo se puede utilizar con los tipos que implementan IDisposable; garantiza que se llamará al método Dispose() incluso si se produce un error.

este código:

using (MyDisposableType x = new MyDisposableType()) 
{ 
    // use x 
} 

es equivalente a esto:

MyDisposableType x = new MyDisposableType(); 
try 
{ 
    // use x 
} 
finally 
{ 
    x.Dispose(); 
} 
+0

Entonces ... úsala en cualquier objeto que implemente IDisposable? ¿Siempre? – danieltalsky

+0

¿Y qué pasa con el manejo de excepciones? – danieltalsky

+0

sería downvoters por favor deje un comentario. Gracias. –

1

lo que dijo Mitch, más ..

Puede utilizar la instrucción using fuera o dentro de un bloque try..catch realmente dependerá de lo que usted está tratando de lograr es decir, si usted razonablemente esperar algo para lanzar una excepción al usar un objeto particular del que planea recuperarse, por ejemplo.

Por la misma razón, también se podía disponer de un objeto que implementa IDisposable en un bloque finally si necesita.

3

que utilizo para pensar en él como "Úsalo cada vez que se implementa IDisposable tipo y que usted no está goinng a necesitar este caso particular más".

1

Utilice usando cuando necesite la eliminación determinística de objetos. Por ejemplo, si abre un archivo, el archivo está bloqueado. A menudo querrá que el archivo se cierre lo antes posible para que otros programas puedan acceder a él. Si no se utiliza usando y escribir algo bajo como:

System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)); 
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)); 
//do smth 

y se produce una excepción durante el "hacer algo bajo" usted no sabe cuando los objetos que operan en un archivo son efectivamente comercializadas y el archivo está cerrado.Con el uso de que sepa con certeza que una vez que ha dejado el utilizando el bloque declaración - ya sea directamente oa través de excepción, el objeto en la declaraciónuseing está dispuesto llamando IDisposable :: Dispose:

using(System.IO.FileStream writeStream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate)) { 
    using(System.IO.BinaryWriter writer = new System.IO.BinaryWriter(writeStream)) { 
     //do stmth 
    } 
} 
+0

No, el objeto * no está * finalizado. Solo está dispuesto. La finalización y la eliminación son muy diferentes. Por lo general, la eliminación suprimirá un finalizador (si hay uno) pero no son lo mismo. –

+0

"Use cuando necesite eliminación de objetos determinísticos". Me parece que si un objeto implementa IDisposable, siempre debe eliminarlo si el objeto ya no se necesita. Al implementar IDisposable, el objeto dice "Yo (podría) usar recursos que deben eliminarse". –

+0

¿Qué sucede si la implementación del objeto en cuestión cambia y el desecho de inmediato se vuelve crítico? ¿Cuál es el beneficio de no desechar justo después del uso? –

1

Puede encerrar un uso dentro de un bloque try/catch y puede encerrar un bloque try/catch dentro de un uso.

Una situación en la que el uso es agradable es cuando se realizan operaciones de bases de datos utilizando DBConnection y DBCommands:

using (SqlConnection conn = new SqlConnection(connectionString)) 
using (SqlCommand command = new SqlCommand(sql, conn)) 
{ 
    // do something here 
} 

Ahora cuando salga de los bloques utilizando está dispuesto a su mando y se cierra la conexión.

1

Lo que Mitch dijo es correcto. Por lo tanto, el uso principal del uso es asegurar que los objetos IDisposable sean eliminados, sin que tenga que codificar una declaración try/catch o try/finally.

Ahora, hay un uso más avanzado que también puede resultarle interesante. Cuando usas una instrucción de uso, el compilador genera un try/finally, y también genera una llamada a Dispose() para ti dentro del fin que genera. Puede usar este método Dispose() como un "gancho" para hacer lo que quiera ... no tiene que estar relacionado con la liberación de recursos.

Por ejemplo, Jeffrey Richter usa esto en un objeto de temporizador que escribió. Se puede hacer algo como esto con él (conceptuales solamente):

using(var x = new Timer()) 
{ 
    // Do the operation you want timed 
} 

// Now, the timer has been stopped, because the 
// compiler-generated call to Dispose() has already 
// occurred, and Jeffrey uses Dispose() to stop 
// the timer. 
+0

Parece un poco complicado. Después del corchete de cierre no tiene acceso a x. ¿Cómo puede aprovechar el temporizador detenido? –

+0

Sí, eso es cierto. En el caso del temporizador de Richter, emite el tiempo transcurrido (y el número de recolecciones de basura que se han producido) a la consola. Así que no tuvo otra necesidad de referenciar el objeto después del corsé de cierre. –

+0

Sin embargo, hay formas de lograr eso también si lo desea. Se podría decir al objeto que active un evento o coloque los resultados en una colección. O puede parametrizar el objeto con una acción lambda para realizar dentro de sí mismo. Pero esto se está alejando demasiado de la pregunta original. –

2

Si desea saber cómo usar creativamente dentro de sus propios diseños, mirar a través de algunos de su propio código para situaciones en las que un poco de código en particular absolutamente debe ejecutarse antes de que salga el bloque adjunto. Estas son las situaciones en las que try/finally o using pueden ayudarlo.

En particular, si ha tratado de lograr esto atrapando todas las excepciones, entonces realmente debe cambiar a try/finally o using en su lugar.

Si el patrón ocurre varias veces, puede crear una clase que implemente IDisposable para capturar el patrón, y que le permita invocar el patrón con la instrucción using. Pero si tiene un caso específico que parece ser único, simplemente use try/finally.

Los dos son muy similares, en realidad - using se especifica en términos de try/finally, pero incluso si sólo tuviéramos using, podríamos construir try/finally nosotros mismos:

public class DisposeAnything : IDisposable 
{ 
    public Action Disposer; 

    public void Dispose() 
    { 
     Disposer(); 
    } 
} 

Ahora usted podría decir:

using (new DisposeAnything 
     { 
      Disposer =() => File.Delete(tempFileName) 
     }) 
{ 
    // blah 
} 

¿Qué es la misma que:

try 
{ 
    // blah 
} 
finally 
{ 
    File.Delete(tempFileName); 
} 

Solo piense en ello como una forma de obtener algún código para ejecutar al salir de un ámbito.

+0

Oye, la parametrización de la lógica de eliminación es una idea interesante. Me imagino que hay muchas formas de hacerlo. Guay. –

+0

Llevado al extremo anterior, ¡no hay mucho que señalar! Pero sí, probablemente haya muchos casos a mitad de camino. Puede encontrar esta discusión interesante: http://incrediblejourneysintotheknown.blogspot.com/2009/02/functional-replacement-for-using.html –

1

Si una clase implementa IDisposable, es probable que sea por una buena razón. Por lo tanto, cualquier clase que implemente IDisposable debe eliminarse.

1

Dado que es lo que se conoce como "azúcar sintáctico" y producirá la misma IL como una construcción de intento/finalmente eliminar, es realmente una buena forma de "corto plazo" de dicho código.

Me gusta usarlo para simplificar secciones de código donde los objetos desechables se utilizan mucho, es decir, el acceso a recursos como archivos y objetos gráficos y quiero asegurarme de que no me olvide de manejar la eliminación del recurso objeto.

0

Me di cuenta de que cuando se sabe que el método Dispose lo hace, los programadores no se molestan en llamarlo ya que parece inútil. Sin embargo, si el objeto implementa IDisposable es (con suerte) por un motivo y las versiones futuras de ese objeto pueden tener código en el método Dispose, así que llámalo siempre.

Cuestiones relacionadas