web-dev-qa-db-fra.com

C # try-catch-else

Une chose qui m’a perturbé avec la gestion des exceptions venant de Python en C # est qu’en C #, il ne semble pas y avoir de moyen de spécifier une clause else. Par exemple, en Python, je pourrais écrire quelque chose comme ceci (Remarque, il ne s'agit que d'un exemple. Je ne demande pas quelle est la meilleure façon de lire un fichier):

try
{
    reader = new StreamReader(path);
}
catch (Exception)
{
    // Uh oh something went wrong with opening the file for reading
}
else
{
    string line = reader.ReadLine();
    char character = line[30];
}

D'après ce que j'ai vu dans la plupart des codes C #, les gens écriraient simplement ce qui suit:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (Exception)
{
    // Uh oh something went wrong, but where?
}

Le problème avec ceci est que je ne veux pas attraper l'exception d'exception provenant du fait que la première ligne du fichier ne doit pas contenir plus de 30 caractères. Je veux seulement attraper les exceptions relatives à la lecture du flux de fichiers. Existe-t-il une construction similaire que je peux utiliser en C # pour obtenir la même chose?

28
Martin Sherburn

Catch une classe spécifique d'exceptions

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (IOException ex)
{
    // Uh oh something went wrong with I/O
}
catch (Exception ex)
{
    // Uh oh something else went wrong
    throw; // unless you're very sure what you're doing here.
}

La deuxième capture est facultative, bien sûr. Et puisque vous ne savez pas ce qui s'est passé, avaler cette exception très générale est très dangereux. 

46
Henk Holterman

Vous pourriez l'écrire comme:

bool success = false;
try {
    reader = new StreamReader(path);
    success = true;
}
catch(Exception) {
    // Uh oh something went wrong with opening the file for reading
}
finally {
    if(success) {
        string line = reader.ReadLine();    
        char character = line[30];
    }
}   
11
Johan Kullbom

Tu peux le faire:

try
{
    reader = new StreamReader(path);
}
catch (Exception)
{
    // Uh oh something went wrong with opening the file for reading
}

string line = reader.ReadLine();
char character = line[30];

Mais bien sûr, vous devrez définir reader dans un état correct ou return en dehors de la méthode.

8
Adrian Godong

Catch des exceptions plus spécifiques.

try {
   reader = new StreamReader(path);
   string line = reader.ReadLine();
   char character = line[30];
}
catch(FileNotFoundException e) {
   // thrown by StreamReader constructor
}
catch(DirectoryNotFoundException e) {
   // thrown by StreamReader constructor
}
catch(IOException e) {
   // some other fatal IO error occured
}

De plus, en général, gérez l’exception la plus spécifique possible et évitez de manipuler le System.Exception de base.

6
jason

Les exceptions sont utilisées différemment dans .NET; ils sont pour des conditions exceptionnelles seulement.

En fait, vous ne devriez pas attraper une exception à moins que vous sachiez ce que cela signifie et que vous puissiez réellement do quelque chose à ce sujet.

4
John Saunders

Vous pouvez avoir plusieurs clauses catch, chacune spécifique au type d'exception que vous souhaitez intercepter. Donc, si vous voulez uniquement intercepter IOExceptions, vous pouvez modifier votre clause catch en ceci:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (IOException)
{    
}

Toute autre chose qu'une exception IOException serait alors propagée dans la pile d'appels. Si vous souhaitez également gérer d'autres exceptions, vous pouvez ajouter plusieurs clauses d'exception, mais vous devez vous assurer qu'elles sont ajoutées de la manière la plus spécifique à la plupart des commandes génériques. Par exemple:

try
{
    reader = new StreamReader(path);
    string line = reader.ReadLine();
    char character = line[30];
}
catch (IOException)
{    
}
catch (Exception)
{
}
2

Plus idiomatiquement, vous utiliseriez l'instruction using pour séparer l'opération d'ouverture de fichier du travail effectué sur les données qu'elle contient (et inclure le nettoyage automatique à la sortie).

try {
  using (reader = new StreamReader(path))
  {
    DoSomethingWith(reader);
  }
} 
catch(IOException ex)
{
  // Log ex here
}

Il est également préférable d'éviter d'attraper toutes les exceptions possibles, comme celles qui vous indiquent que le moteur d'exécution est sur le point d'expirer.

2
Steve Gilham

Vous pouvez également imbriquer vos déclarations try

2
David

Après avoir vu les autres solutions suggérées, voici mon approche:

try {
    reader = new StreamReader(path);
}
catch(Exception ex) {
    // Uh oh something went wrong with opening the file stream
    MyOpeningFileStreamException newEx = new MyOpeningFileStreamException();
    newEx.InnerException = ex;
    throw(newEx);
}
    string line = reader.ReadLine();
    char character = line[30];

Bien entendu, cela n'a de sens que si vous êtes intéressé par toute exceptions levées en ouvrant le flux de fichiers (à titre d'exemple ici) en dehors de de toutes les autres exceptions de l'application. À un niveau plus élevé de l'application, vous devez alors gérer votre MyOpeningFileStreamException comme bon vous semble.

En raison des exceptions non contrôlées, vous ne pouvez jamais être sûr à 100% qu'il ne suffira pas de capturer uniquement IOException du bloc de code; le StreamReader pourra décider de lancer un autre type d'exception également, maintenant ou à l'avenir.

1
Tiberiu Ana

Existe-t-il une construction similaire que je peux utiliser en C # Pour obtenir la même chose?

Non. 

Enveloppez votre accesseur d’index avec une instruction "if" qui est la meilleure solution dans votre cas en termes de performances et de lisibilité.

if (line.length > 30) {
   char character = line [30];
} 
1
Christian Birkl

Il se peut que try { ... } catch { ... } else { ... } ne soit pas pris en charge en natif, mais si vous êtes prêt à supporter le problème lié à l’utilisation d’une solution de contournement, l’exemple ci-dessous peut être intéressant:

using System;

public class Test
{
    public static void Main()
    {
        Example("ksEE5A.exe");
    }

    public static char Example(string path) {
        var reader = default(System.IO.StreamReader);
        var line = default(string);
        var character = default(char);
        TryElse(
            delegate {
                Console.WriteLine("Trying to open StreamReader ...");
                reader = new System.IO.StreamReader(path);
            },
            delegate {
                Console.WriteLine("Success!");
                line = reader.ReadLine();
                character = line[30];
            },
            null,
            new Case(typeof(NullReferenceException), error => {
                Console.WriteLine("Something was null and should not have been.");
                Console.WriteLine("The line variable could not cause this error.");
            }),
            new Case(typeof(System.IO.FileNotFoundException), error => {
                Console.WriteLine("File could not be found:");
                Console.WriteLine(path);
            }),
            new Case(typeof(Exception), error => {
                Console.WriteLine("There was an error:");
                Console.WriteLine(error);
            }));
        return character;
    }

    public static void TryElse(Action pyTry, Action pyElse, Action pyFinally, params Case[] pyExcept) {
        if (pyElse != null && pyExcept.Length < 1) {
            throw new ArgumentException(@"there must be exception handlers if else is specified", nameof(pyExcept));
        }
        var doElse = false;
        var savedError = default(Exception);
        try {
            try {
                pyTry();
                doElse = true;
            } catch (Exception error) {
                savedError = error;
                foreach (var handler in pyExcept) {
                    if (handler.IsMatch(error)) {
                        handler.Process(error);
                        savedError = null;
                        break;
                    }
                }
            }
            if (doElse) {
                pyElse();
            }
        } catch (Exception error) {
            savedError = error;
        }
        pyFinally?.Invoke();
        if (savedError != null) {
            throw savedError;
        }
    }
}

public class Case {
    private Type ExceptionType { get; }
    public Action<Exception> Process { get; }
    private Func<Exception, bool> When { get; }

    public Case(Type exceptionType, Action<Exception> handler, Func<Exception, bool> when = null) {
        if (!typeof(Exception).IsAssignableFrom(exceptionType)) {
            throw new ArgumentException(@"exceptionType must be a type of exception", nameof(exceptionType));
        }
        this.ExceptionType = exceptionType;
        this.Process = handler;
        this.When = when;
    }

    public bool IsMatch(Exception error) {
        return this.ExceptionType.IsInstanceOfType(error) && (this.When?.Invoke(error) ?? true);
    }
}
0
Noctis Skytower

On dirait que vous voulez faire la deuxième chose seulement si la première chose a réussi. Et peut-être qu'attraper différentes classes d'exceptions n'est pas approprié, par exemple si les deux instructions peuvent lancer la même classe d'exceptions. 

try
{
    reader1 = new StreamReader(path1);
    // if we got this far, path 1 succeded, so try path2
    try
    {
        reader2 = new StreamReader(path2);

    }
    catch (OIException ex)
    {
        // Uh oh something went wrong with opening the file2 for reading
        // Nevertheless, have a look at file1. Its fine!
    }
}
catch (OIException ex)
{
    // Uh oh something went wrong with opening the file1 for reading.
    // So I didn't even try to open file2
}
0
Julian Mann

J'ai pris la liberté de transformer un peu votre code pour démontrer quelques points importants.

La construction using est utilisée pour ouvrir le fichier. Si une exception est levée, vous devrez vous rappeler de fermer le fichier même si vous ne l'attrapez pas. Cela peut être fait en utilisant une construction try { } catch () { } finally { }, mais la directive using est bien meilleure pour cela. Il garantit que lorsque la portée du bloc using se termine, la variable créée à l'intérieur sera supprimée. Pour un fichier, cela signifie qu'il sera fermé.

En étudiant la documentation du constructeur StreamReader et de la méthode ReadLine, vous pouvez voir les exceptions que vous pouvez espérer voir levées. Vous pouvez alors attraper ceux que vous trouvez appropriés. Notez que la liste documentée des exceptions n'est pas toujours complète.

// May throw FileNotFoundException, DirectoryNotFoundException,
// IOException and more.
try {
  using (StreamReader streamReader = new StreamReader(path)) {
    try {
      String line;
      // May throw IOException.
      while ((line = streamReader.ReadLine()) != null) {
        // May throw IndexOutOfRangeException.
        Char c = line[30];
        Console.WriteLine(c);
      }
    }
    catch (IOException ex) {
      Console.WriteLine("Error reading file: " + ex.Message);
    }
  }
}
catch (FileNotFoundException ex) {
  Console.WriteLine("File does not exists: " + ex.Message);
}
catch (DirectoryNotFoundException ex) {
  Console.WriteLine("Invalid path: " + ex.Message);
}
catch (IOException ex) {
  Console.WriteLine("Error reading file: " + ex.Message);
}
0
Martin Liversage