2009-09-02 12 views
15

La sentencia using (...) es azúcar sintáctica para try {} finally {}.Usando statement y try-catch() - finalmente ¿repetición?

Pero si luego tengo una instrucción using, como a continuación:

using (FileStream fs = File.Open(path)) 
{ 


} 

Ahora quiero coger las excepciones que la apertura de este archivo podría causar (y este es el código riesgo bastante alto, ya que puede fallar debido a la el medio ambiente), pero si escribo try-catch adentro, ¿no sería repetición? Cuando el código se compila en IL, ¿supongo que la repetición se borrará cuando el código esté JITted?

Sin embargo, me gustaría ver las excepciones que puede causar un archivo (así que debería cerrar el try-catch fuera del alcance de la sentencia using), y también las excepciones para lo que haga dentro del bloque using así que debería agregar el try-catch dentro del bloque.

Parece que estoy añadiendo mucha repetición a lo que la CLR probablemente hace dentro. ¿El CLR agrega cláusulas catch?

Mi colega argumentó que una declaración de uso es desordenada (pero esto se debió a que una sola línea era ligeramente larga debido a la codificación difícil ya que necesitaba cambiarlas muy rápido y no tenía acceso a otras partes del código base). Dicho colega no usa el enunciado using, pero ¿existe alguna diferencia funcional entre el enunciado using y try-finally/try-catch-finally? Vi un caso de esto en el que los servicios de WCF tienen un caso de esquineros no muy conocido sobre el uso de valores devueltos y finalmente (algo sobre finalmente). La solución fue usar un bloque de verificación. ¿Hay algo como esto en C#?

En otro aspecto, ¿todos los tipos que implementan IDisposale propietarios de recursos no administrados? Una discusión con mi amigo señaló la respuesta a ser no. (También he leído algunos hilos en la sección de uso de este foro, algunos muy buenos conocimientos allí).

Respuesta

7

Puede implementar el patrón usted mismo si necesita manejar algunas excepciones si realmente lo desea. Personalmente, todavía me parece más simple (y más importante, más claro) simplemente envolver el uso en el bloque try/catch.

Pero si lo haces tú mismo, asegúrate de hacerlo bien. El bloque Using también crea un bloque de ámbito anónimo para que su variable sea elegible para la recopilación antes. El método .Dispose() llamado al final del bloque Using solo limpia recursos no administrados, por lo que la memoria que guarde su objeto puede esperar un poco más. No es probable que sea una gran preocupación, pero vale la pena recordarlo por las dudas.

lo tanto, para hacer una adaptación directa del patrón, el código tiene que mirar de la misma familia:

{ 
    FileStream fs; 
    try 
    { 
     fs = File.Open(path); 

    } 
    catch (FileNotFoundException e) { /* ... */ } 
    catch (IOException e) { /* ... */ } 
    catch (Exception e) {/* ... */} 
    finally 
    { 
     if (fs != null) fs.Dispose(); 
    } 
} 

Personalmente, me gustaría ver Using ampliado que apoye Catch y Finally bloques. Como ya están realizando una transformación en el código, no parece que esto agregue mucha complejidad adicional.

+0

El uso ¿soporta finalmente aunque? ¿Dónde se enteró sobre el uso, usando un bloque de alcance anónimo? Me gustaría saber más sobre esto. Entonces, cuando abro un archivo en un bloque que usa (por ejemplo, FileSream.Open()), esta excepción aparecerá. Si la instrucción using implementa try/finally, tengo que ajustar eso en try/catch solo para tener el catch. – dotnetdev

4

Si usted necesita para manejar de forma explícita diferentes casos excepcionales que puedan ocurrir durante la declaración, podría reemplazar using con try/catch/finally y llame Dispose() explícitamente en el fin. O puede poner un try/catch alrededor del bloque using para separar las circunstancias excepcionales de garantizar la eliminación.

La única cosausing hace es asegurarse de que se llama a Dispose() incluso si se lanza una excepción dentro del bloque. Esa es una implementación muy limitada y altamente específica de la estructura general try/[catch]/finally.

Lo importante es que ninguna de estas opciones tiene un impacto real: siempre que haga lo que necesita, es legible y comprensible, ¿a quién le importa? ¡No es como si un intento adicional fuera un cuello de botella o algo así!

Para responder a su última pregunta, no, IDisposable definitivamente no significa necesariamente que el implementador tenga controles para recursos no administrados. Es un patrón mucho más simple y más genérico que eso. He aquí un ejemplo útil:

public class Timer : IDisposable 
{ 
    internal Stopwatch _stopwatch; 
    public Timer() 
    { 
     this._stopwatch = new Stopwatch(); 
     this._stopwatch.Start(); 
    } 

    public void Dispose() 
    { 
     this._stopwatch.Stop(); 
    } 
} 

Podemos usar esto para el tiempo las cosas sin tener que depender de forma explícita en el arranque y parada de ser llamado mediante el uso de:

using(Timer timer = new Timer()) 
{ 
    //do stuff 
} 
4

La mayor diferencia entre usar y probar-finalmente es ese uso solo llamará al método Dispose().Si implementa su propio bloque finally puede hacer otra lógica, por ejemplo, el registro, que no se incluiría en el uso.

+0

+1 por falta de definición –

7

Mi preferencia sería mantener la instrucción using y envolverla en try/catch. El try/catch externo detectará las excepciones que necesite para vigilar, mientras ignora el Dispose(). Esto tiene la ventaja adicional de protegerlo de usted mismo si lo refactoriza más tarde para mover el try/catch a otro lugar (como en una función de llamada).

En cuanto a su pregunta sobre IDisposable: Cualquiera puede implementar esto por cualquier razón que les guste. No hay ninguna razón técnica para que se limite a recursos no administrados. (Si se debe limitarse a recursos no administrados en su código es una pregunta diferente).

3

La construcción using es la abreviatura de un bloque try/finally. Es decir, el compilador genera:

FileStream fs = null; 
try 
{ 
    fs = File.Open(path); 
    // ... 
} 
finally 
{ 
    if (fs != null) 
     fs.Dispose(); 
} 

Por lo tanto, es adecuado para usar using si usted no necesita un catch, pero si lo hace, entonces sólo debe utilizar una normal de try/catch/finally.

+2

No hay nada de malo en envolver 'using' en' try'/'catch'. Es más claro que "finalmente" porque es inmediatamente obvio qué recursos se liberan después de que el bloque sale sin tener que mirar "finalmente". –