try / catch + utilisation, syntaxe correcte

189

Laquelle:

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

OU

try
{
   using (var myObject = new MyClass())
   {
      // something here...
   }
}
catch(Exception ex)
{
   // Handle exception
}
Xaqron
la source
7
Juste une note: il faut faire attention à ne capturer que les exceptions qui peuvent réellement être gérées (corrigées), sauf pour les journaliser ou les encapsuler.
John Saunders le
1
S'il vous plaît garder à l' esprit que aussi le dernier }de la usingdéclaration peut jeter une exception comme l'a rappelé ici .
Giulio Caccin
1
TIL que le débogueur (dans VS) n'appelle pas la méthode dispose si vous utilisez le premier bloc de code. Étant donné que l'instruction using elle-même peut lever une exception, elle m'aide à utiliser le deuxième bloc pour garantir la finallyméthode implicite appelée la méthode dispose.
ShooShoSha

Réponses:

98

Je préfère le second. Peut également piéger les erreurs liées à la création de l'objet.

Jonathan Wood
la source
11
Je ne suis pas d'accord avec ce conseil. Si vous vous attendez à ce que la création d'objet génère une erreur, toute gestion de cette exception doit sortir. S'il y a une question sur la destination de la gestion, alors l'exception attendue doit être autre chose - à moins que vous ne recommandiez de capturer une exception aléatoire qui pourrait ou non être anticipée, qui est un anti-pattern classique (en dehors d'un traitement ou le gestionnaire d'exceptions non gérées du thread).
Jeffrey L Whitledge
1
@Jeffrey: L'approche que j'ai décrite m'a bien servi et je le fais depuis longtemps. Personne ne dit rien attendre la création d'objets à l' échec. Mais en encapsulant une opération qui pourrait potentiellement échouer dans un trybloc, ce qui vous permet d'afficher un message d'erreur si quelque chose échoue, le programme a maintenant la capacité de récupérer et d'informer l'utilisateur.
Jonathan Wood
Votre réponse est correcte mais continue de suggérer que le try / catch doit être là (immédiatement) à tout moment.
Henk Holterman
17
Je pense que la première a également du mérite, envisagez une transaction DB using( DBConnection conn = DBFactory.getConnection())qui devrait être annulée en cas d'exception. Il me semble que les deux ont leur place.
wfoster
1
Cela interceptera également les erreurs liées à l' élimination de l'objet.
Ahmad Ibrahim le
39

Étant donné qu'un bloc d'utilisation n'est qu'une simplification de la syntaxe d'un essai / enfin ( MSDN ), personnellement, j'irais avec ce qui suit, même si je doute que ce soit significativement différent de votre deuxième option:

MyClass myObject = null;
try {
  myObject = new MyClass();
  //important stuff
} catch (Exception ex) {
  //handle exception
} finally {
  if(myObject is IDisposable) myObject.Dispose();
}
chezy525
la source
4
Pourquoi pensez-vous que l'ajout d'un finallybloc est préférable à l' usinginstruction?
Cody Gray
10
L'ajout d'un finallybloc qui supprime un objet IDisposable est ce qu'une usinginstruction fait. Personnellement, j'aime ça au lieu du usingbloc intégré parce que je pense qu'il indique plus proprement où tout se passe, et que tout est au même «niveau». J'aime aussi cela plus que plusieurs usingblocs intégrés ... mais c'est juste ma préférence.
chezy525
8
Si vous implémentez beaucoup de gestion des exceptions, vous devez vraiment apprécier la saisie! Ce mot-clé "using" existe depuis un certain temps et sa signification est assez claire pour moi. Et son utilisation aide à rendre le reste de mon code plus clair en minimisant l'encombrement.
Jonathan Wood
2
Ceci est une erreur. L'objet doit être instancié en dehors de l' tryinstruction pour être supprimé dans l' finallyinstruction; sinon, il lancera une erreur du compilateur: "Utilisation de la variable locale non affectée 'myObject'"
Steve Konves
3
Techniquement, cela ne compilera pas non plus. Cannot assign null to implicitly-typed local variable;) Mais je sais ce que vous voulez dire et je préférerais personnellement cela à l'imbrication d'un bloc d'utilisation.
Connell
20

Ça dépend. Si vous utilisez Windows Communication Foundation (WCF), using(...) { try... }ne fonctionnera pas correctement si le proxy dans l' usinginstruction est à l'état d'exception, c'est-à-dire que la suppression de ce proxy provoquera une autre exception.

Personnellement, je crois en une approche de manipulation minimale, c'est-à-dire ne gérez que l'exception dont vous avez connaissance au moment de l'exécution. En d'autres termes, si vous savez que l'initialisation d'une variable dans usingpeut lever une exception particulière, je l'enveloppe avec try-catch. De même, si usingquelque chose peut se produire dans le corps, qui n'est pas directement lié à la variable in using, alors je l'enveloppe avec un autre trypour cette exception particulière. J'utilise rarement Exceptiondans mes catches.

Mais j'aime bien IDisposableet usingdonc je suis peut-être partial.

Schultz9999
la source
19

Si votre instruction catch a besoin d'accéder à la variable déclarée dans une instruction using, alors inside est votre seule option.

Si votre instruction catch a besoin de l'objet référencé dans using avant qu'il ne soit supprimé, alors inside est votre seule option.

Si votre instruction catch prend une action de durée inconnue, comme afficher un message à l'utilisateur, et que vous souhaitez disposer de vos ressources avant que cela ne se produise, alors l'extérieur est votre meilleure option.

Chaque fois que j'ai un scénario similaire à celui-ci, le bloc try-catch est généralement dans une méthode différente plus haut dans la pile d'appels de l'utilisation. Il n'est pas courant pour une méthode de savoir comment gérer les exceptions qui s'y produisent de cette manière.

Ma recommandation générale est donc à l'extérieur - bien à l'extérieur.

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
la source
10

Les deux sont une syntaxe valide. Cela dépend vraiment de ce que vous voulez faire: si vous voulez attraper des erreurs liées à la création / suppression de l'objet, utilisez le second. Sinon, utilisez le premier.

Smashery
la source
8

Il y a une chose importante que j'appellerai ici: la première ne détectera aucune exception découlant de l'appel du MyClassconstructeur.

Madhur Ahuja
la source
3

À partir de C # 8.0, je préfère utiliser le second comme celui-ci

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

    public void Dispose()
    {
    }
}

puis

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
la source
1

Si l'objet que vous initialisez dans le bloc Using () peut lever une exception, vous devriez opter pour la deuxième syntaxe sinon les deux sont également valides.

Dans mon scénario, je devais ouvrir un fichier et je passais filePath dans le constructeur de l'objet que j'étais en train d'initialiser dans le bloc Using () et cela pouvait lever une exception si le filePath est faux / vide. Donc, dans ce cas, la deuxième syntaxe a du sens.

Mon exemple de code: -

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

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

À partir de C # 8.0 , vous pouvez simplifier les usinginstructions sous certaines conditions pour vous débarrasser du bloc imbriqué, puis cela s'applique uniquement au bloc englobant.

Ainsi, vos deux exemples peuvent être réduits à:

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

Et:

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

Les deux sont assez clairs; et puis cela réduit le choix entre les deux à une question de ce que vous voulez que la portée de l'objet soit, où vous voulez gérer les erreurs d'instanciation, et quand vous voulez en disposer.

Jason C
la source