2009-11-12 15 views
10

Entiendo que el punto de "usar" es garantizar que se invocará el método Dispose del objeto. Pero, ¿cómo debería manejarse una excepción dentro de una declaración de "uso"? Si hay una excepción, necesito ajustar mi declaración de "uso" en una captura de prueba. Por ejemplo:¿Por qué 'usar' no tiene un bloque catch?


permite decir que hay una excepción creada en la creación del objeto dentro del medio del parámetro

try 
{ 
    // Exception in using parameter 
    using (SqlConnection connection = new SqlConnection("LippertTheLeopard")) 
    { 
     connection.Open(); 
    } 
} 
catch (Exception ex) 
{ 

} 

o una excepción en el uso de alcance

using (SqlConnection connection = new SqlConnection()) 
{ 
    try 
    { 
     connection.Open(); 
    } 
    catch (Exception ex) 
    { 

    } 
} 

Parece como si Ya tengo que manejar una excepción con una captura de prueba, que quizás debería manejar la eliminación del objeto también. En este caso, la declaración de "uso" no parece ayudarme en absoluto. ¿Cómo manejo adecuadamente una excepción con la declaración "using"? ¿Hay un mejor enfoque para esto que me falta?

SqlConnection connection2 = null; 
try 
{ 
    connection2 = new SqlConnection("z"); 
    connection2.Open(); 
} 
catch (Exception ex) 
{ 

} 
finally 
{ 
    IDisposable disp = connection2 as IDisposable; 
    if (disp != null) 
    { 
     disp.Dispose(); 
    } 
} 

Podría el "uso de" palabras clave de sintaxis ser un poco más dulce ...
Seguro que sería bueno tener esto:

using (SqlConnection connection = new SqlConnection()) 
{ 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // What went wrong? Well at least connection is Disposed 
} 

Respuesta

5

He tenido lugares donde esto sería útil. Pero más a menudo, cuando quiero hacer esto, resulta que el problema está en mi diseño; Estoy tratando de manejar la excepción en el lugar equivocado.

En su lugar, tengo que permitir que pase al siguiente nivel — manejarlo en la función que llamó a este código, en lugar de hacerlo allí mismo.

+0

+1 Este es un gran punto. La excepción probablemente deba manejarse en el siguiente nivel. – SwDevMan81

12

using no tiene nada que ver con el manejo de errores. Es la abreviatura de "llamar a Dispose cuando salgas de este bloque". Su segundo ejemplo de código es perfectamente aceptable ... ¿por qué meterse con lo que funciona?

+0

El punto de la pregunta es, ¿por qué el uso de no tiene un bloque catch opcional? Es más retórico a menos que trabajes en Microsoft. – Chap

+0

No haría el segundo ejemplo. imo es un ejemplo de manejo de un error en el nivel incorrecto. Déjalo 'burbujear' hasta la función de llamada. –

+1

@ Joel estuvo de acuerdo en el mayor alcance de las cosas, esa es la manera en que lo haría si tuviera una razón para hacer un manejo de errores en ese nivel. –

2

Una idea interesante, pero sería hacer la siguiente un poco confuso:

using (SqlConnection connection = new SqlConnection()) 
using (SqlCommand cmd = new SqlCommand()) 
{ 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // Is connection valid? Is cmd valid? how would you tell? 
    // if the ctor of either throw do I get here? 
} 
+1

Su ejemplo no es válido. Las instrucciones de uso "apiladas" no son una función de idioma. Tu código es equivalente a anidar el segundo usando dentro del bloque del primer uso. Esto no es diferente de escribir dos sentencias if de forma consecutiva sin llaves. –

+1

Si desea ver cada excepción de forma individual, puede anidar los bloques de uso. De hecho, me gusta la idea del OP, aunque estoy seguro de que nunca lo implementarán en el idioma. No es lo suficientemente útil y potencialmente confuso. –

+2

@bytenik: eso es irrelevante. Este código aún podría confundir a las personas. No importa que no sea una característica especial. –

8

El uso de bloque es simplemente azúcar sintáctico para un try-bloque finally. Si necesita una cláusula catch, sólo tiene que utilizar un try-catch-finally:

SqlConnection connection; 
try 
{ 
    connection = new SqlConnection(); 
    connection.Open(); 
} 
catch(Exception ex) 
{ 
    // handle 
} 
finally 
{ 
    if (connection != null) 
    { 
     connection.Dispose(); 
    } 
} 

Sí, esto es más código que el teórico "por medio de la captura incidental"; Juzgo que los desarrolladores de idiomas no consideraron esto una prioridad muy alta, y no puedo decir que alguna vez haya sentido su pérdida.

+0

Saldrá del bloque de instrucciones de uso cuando se produzca una excepción, por lo que una prueba/captura es mejor. – ChadNC

+2

@ChadNC: aparentemente no entiende 'usar'; lo protege de las excepciones y eliminará el objeto de todos modos. –

27

Porque estaría 'ocultando' la funcionalidad adicional dentro de una palabra clave no relacionada.

Sin embargo siempre se puede escribir de esta manera

using (...) try 
{ 
} 
catch (...) 
{ 
} 

Y de esta manera la línea corresponde a sus intenciones - una declaración usando esa también es una oportunidad

+0

Esa es una forma breve y limpia de hacerlo. –

+0

Bueno, eso parece manejar cualquier error dentro del uso, aunque no cubre la excepción en el parámetro de uso. Aunque es bueno saber – SwDevMan81

+1

Ooh, me gusta esto. Ojalá pudiera votarlo dos veces. –

2

Usted está mezclando las preocupaciones en mi opinión. La gestión de recursos (es decir, la eliminación de objetos) está completamente separada del manejo de excepciones. El mapeo de uno a uno que describa en su pregunta es solo un caso muy especial. Por lo general, el manejo de excepciones no se realizará en el mismo lugar donde termina el uso. O puede tener varias regiones try-catch dentro de un bloque de uso. O ...

+1

el propósito de un bloqueo final es asegurarse de que los recursos se limpien. en el caso de usar {try {} catch {} finally {}} el uso se vuelve redundante –

+0

Sí, pero ¿por qué usar 'finally'? En la mayoría de los casos, ajustar 'try/catch' dentro de' using' es más claro _y_ aún más corto. –

0

Le recomiendo que use los ejemplos # 1 y # 2 combinados. La razón es que su instrucción de uso podría leer un archivo, por ejemplo, y lanzar una excepción (es decir, archivo no encontrado). Si no lo captas, entonces tienes una excepción no controlada. Al poner el bloque de captura de prueba dentro del bloque de uso, solo se detectarán las excepciones que ocurran después de que se haya ejecutado la sentencia de utilización. Una combinación de tu ejemplo uno y dos es lo mejor en mi humilde opinión.

0

El propósito de la instrucción "using" es asegurar que ocurra algún tipo de operación de limpieza cuando la ejecución salga de un bloque de código, independientemente de si esa salida es por fall-through, una excepción o return. Cuando el bloque sale por cualquiera de esos medios, llamará al Dispose en el parámetro using. El bloque existe, en cierto sentido, para el beneficio de lo que se está especificando como el parámetro using, y en general a esa cosa no le importará por qué se salió del bloque.

Hay un par de casos inusuales para los cuales las disposiciones pueden ser útiles; su nivel de utilidad adicional sería muy inferior al proporcionado por tener using en primer lugar (aunque podría decirse que es mejor que algunas otras características que los implementadores consideran adecuado):

(1) Hay un patrón muy común en los constructores o fábricas de objetos que encapsulan otros objetos IDisposable; si el constructor o fábrica sale por una excepción, los objetos encapsulados deben ser Dispose d, pero si sale por return no deberían. Actualmente, dicho comportamiento debe implementarse a través del try/catch, o combinando try/finally con un indicador, pero sería en mi humilde opinión útil si hubiera una variación de using que solo llamaría a Dispose cuando saliera por excepción, o bien un keep using declaración que anularía el temporal empleado por la declaración using para contener el objeto que necesitaba eliminación (ya que using no puede ir precedido de un identificador, tal característica podría agregarse de una manera algo similar a yield return).

(2) En algunos casos, sería útil si la palabra clave finally se extendiera para aceptar un argumento Exception; mantendría la excepción que causó la salida de la cláusula protegida (si existe), o null si la cláusula protegida sale normalmente (a través de retorno o caída), y si el bloque using podría hacer uso de interface IDisposeExOnly {void DisposeEx(Exception ex);} y Interface IDisposeEx : IDisposable, IDisposableExOnly {} (en compilación) tiempo, DisposeEx() seleccionado si está implementado, o Dispose() de lo contrario). Esto podría permitir que los objetos basados ​​en transacciones admitan de forma segura la confirmación automática (es decir, realizar la confirmación si la excepción transferida es null, o retrotracción si no es nula) y también permitiría un mejor registro en situaciones donde Dispose falla como consecuencia de un problema dentro de la cláusula guardada (lo correcto sería que Dispose lanzara una excepción que encapsula tanto la excepción que estaba pendiente cuando se llamó como la excepción que ocurrió como consecuencia, pero actualmente no hay una forma clara de hacerlo) ese).

No sé si Microsoft alguna vez agregará tales características; el primero y la primera parte del segundo se manejarían completamente en el nivel del idioma. La última parte del segundo sería en el nivel del Marco.

Cuestiones relacionadas