try / catch + using, sintaxis correcta

189

Cúal:

using (var myObject = new MyClass())
{
   try
   {
      // something here...
   }
   catch(Exception ex)
   {
      // Handle exception
   }
}

O

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}
Xaqron
fuente
77
Solo una nota: se debe tener cuidado de detectar solo las excepciones que realmente se pueden manejar (corregir), excepto para el registro o la envoltura.
John Saunders
1
Tenga en cuenta que también lo último }de la usingdeclaración puede arrojar una excepción como se recuerda aquí .
Giulio Caccin
1
TIL que el depurador (en VS) no llamará al método de eliminación si usa el primer bloque de código. Debido a que la declaración de uso en sí misma puede arrojar una excepción, me ayuda a usar el segundo bloque para asegurar que el implícito finallyllame al método de eliminación.
ShooShoSha

Respuestas:

98

Prefiero el segundo. También puede atrapar errores relacionados con la creación del objeto.

Jonathan Wood
fuente
11
No estoy de acuerdo con este consejo. Si espera que la creación del objeto arroje un error, cualquier manejo de esa excepción debe realizarse fuera. Si hay alguna pregunta acerca de dónde debe ir el manejo, entonces la excepción que se espera debe ser otra cosa, a menos que esté abogando por la captura de cualquier excepción aleatoria que pueda anticiparse o no, que es un antipatrón clásico (fuera de un proceso o controlador de excepciones no manejadas del subproceso).
Jeffrey L Whitledge
1
@Jeffrey: El enfoque que describí me ha servido bien y lo he estado haciendo durante mucho tiempo. Nadie dijo nada acerca de esperar que fallara la creación de objetos. Pero al ajustar una operación que podría fallar potencialmente en un trybloque, lo que le permite mostrar un mensaje de error si algo falla, el programa ahora tiene la capacidad de recuperarse e informar al usuario.
Jonathan Wood
Su respuesta es correcta, pero sigue la sugerencia de que el try / catch tiene que estar allí (inmediatamente) en todo momento.
Henk Holterman
17
Creo que el primero también tiene mérito, considere una transacción de base de datos using( DBConnection conn = DBFactory.getConnection())que debería revertirse en caso de que ocurriera una excepción. Me parece que ambos tienen su lugar.
wfoster
1
Eso también atrapará errores relacionados con la eliminación del objeto.
Ahmad Ibrahim
39

Dado que un bloque de uso es solo una simplificación de sintaxis de un intento / finalmente ( MSDN ), personalmente iría con lo siguiente, aunque dudo que sea significativamente diferente a su segunda opción:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}
chezy525
fuente
44
¿Por qué crees que finallyes preferible agregar un bloque a la usingdeclaración?
Cody Gray
10
Agregar un finallybloque que disponga de un objeto IDisposable es lo que hace una usinginstrucción. Personalmente, me gusta esto en lugar del usingbloque incrustado porque creo que establece más claramente dónde está sucediendo todo, y que todo está en el mismo "nivel". También me gusta más que varios usingbloques incrustados ... pero todo es solo mi preferencia.
chezy525
8
Si implementa mucho manejo de excepciones, ¡realmente debe disfrutar escribiendo! Esa palabra clave "usar" ha existido por un tiempo y su significado es bastante claro para mí. Y usarlo ayuda a aclarar el resto de mi código al mantener la cantidad de desorden al mínimo.
Jonathan Wood
2
Esto es incorrecto. El objeto debe ser instanciado fuera de la trydeclaración para que se elimine dentro de la finallydeclaración; de lo contrario, arrojará un error de compilación: "Uso de la variable local no asignada 'myObject'"
Steve Konves
3
Técnicamente, eso tampoco compilará. Cannot assign null to implicitly-typed local variable;) Pero sé lo que quieres decir y personalmente preferiría esto a anidar un bloque de uso.
Connell
20

Depende. Si está utilizando Windows Communication Foundation (WCF), using(...) { try... }no funcionará correctamente si el proxy en la usingdeclaración está en estado de excepción, es decir, eliminar este proxy causará otra excepción.

Personalmente, creo en un enfoque de manejo mínimo, es decir, manejar solo una excepción que conozca en el punto de ejecución. En otras palabras, si sabe que la inicialización de una variable usingpuede generar una excepción en particular, la envolveré try-catch. Del mismo modo, si dentro del usingcuerpo puede suceder algo, que no está directamente relacionado con la variable using, entonces lo envuelvo con otro trypara esa excepción en particular. Raramente uso Exceptionen mi catches.

Pero sí me gusta IDisposabley, usingaunque quizás sea parcial.

Schultz9999
fuente
19

Si su declaración catch necesita acceder a la variable declarada en una declaración using, entonces dentro es su única opción.

Si su declaración catch necesita el objeto al que se hace referencia antes de desecharlo, entonces su única opción es dentro.

Si su declaración catch toma una acción de duración desconocida, como mostrar un mensaje al usuario, y desea deshacerse de sus recursos antes de que eso suceda, entonces afuera es su mejor opción.

Cada vez que tengo un escenario similar a este, el bloque try-catch generalmente está en un método diferente más arriba de la pila de llamadas que el uso. No es típico que un método sepa cómo manejar excepciones que ocurren dentro de él de esta manera.

Así que mi recomendación general es afuera, muy afuera.

private void saveButton_Click(object sender, EventArgs args)
{
    try
    {
        SaveFile(myFile); // The using statement will appear somewhere in here.
    }
    catch (IOException ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Jeffrey L Whitledge
fuente
10

Ambos son sintaxis válida. Realmente se reduce a lo que desea hacer: si desea detectar errores relacionados con la creación / eliminación del objeto, use el segundo. Si no, usa el primero.

Smashery
fuente
8

Hay una cosa importante que llamaré aquí: la primera no detectará ninguna excepción derivada de llamar al MyClassconstructor.

Madhur Ahuja
fuente
3

Desde C # 8.0, prefiero usar el segundo igual así

public class Person : IDisposable
{
    public Person()
    {
        int a = 0;
        int b = Id / a;
    }
    public int Id { get; set; }

    public void Dispose()
    {
    }
}

y entonces

static void Main(string[] args)
    {

        try
        {
            using var person = new Person();
        }
        catch (Exception ex) when
        (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
        ex.TargetSite.MemberType == System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Constructor Person");
        }
        catch (Exception ex) when
       (ex.TargetSite.DeclaringType.Name == nameof(Person) &&
       ex.TargetSite.MemberType != System.Reflection.MemberTypes.Constructor)
        {
            Debug.Write("Error Person");
        }
        catch (Exception ex)
        {
            Debug.Write(ex.Message);
        }
        finally
        {
            Debug.Write("finally");
        }
    }
Reza Jenabi
fuente
1

Si el objeto que está inicializando en el bloque Using () podría arrojar alguna excepción, entonces debería optar por la segunda sintaxis, de lo contrario, ambas serían igualmente válidas.

En mi escenario, tuve que abrir un archivo y estaba pasando filePath en el constructor del objeto que estaba inicializando en el bloque Using () y podría arrojar una excepción si filePath está incorrecto / vacío. Entonces, en este caso, la segunda sintaxis tiene sentido.

Mi código de muestra: -

try
{
    using (var obj= new MyClass("fileName.extension"))
    {

    }
}
catch(Exception ex)
{
     //Take actions according to the exception.
}
Ankur Arora
fuente
1

A partir de C # 8.0 , puede simplificar las usingdeclaraciones en algunas condiciones para deshacerse del bloque anidado, y luego solo se aplica al bloque que lo encierra.

Entonces sus dos ejemplos se pueden reducir a:

using var myObject = new MyClass();
try
{
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Y:

try
{
   using var myObject = new MyClass();
   // something here...
}
catch(Exception ex)
{
   // Handle exception
}

Ambos son bastante claros; y luego eso reduce la elección entre los dos a una cuestión de lo que quiere que sea el alcance del objeto, dónde desea manejar los errores de instanciación y cuándo desea deshacerse de él.

Jason C
fuente