C# Language Création d'exceptions personnalisées


Exemple

Vous êtes autorisé à implémenter des exceptions personnalisées pouvant être lancées comme toute autre exception. Cela a du sens lorsque vous voulez distinguer vos exceptions des autres erreurs lors de l'exécution.

Dans cet exemple, nous allons créer une exception personnalisée pour gérer clairement les problèmes éventuels de l'application tout en analysant une entrée complexe.

Création d'une classe d'exception personnalisée

Pour créer une exception personnalisée, créez une sous-classe d' Exception :

public class ParserException : Exception
{
    public ParserException() : 
      base("The parsing went wrong and we have no additional information.") { }
}

Les exceptions personnalisées deviennent très utiles lorsque vous souhaitez fournir des informations supplémentaires au receveur:

public class ParserException : Exception
{
    public ParserException(string fileName, int lineNumber) : 
      base($"Parser error in {fileName}:{lineNumber}") 
    {
      FileName = fileName;
      LineNumber = lineNumber;
    }
    public string FileName {get; private set;}
    public int LineNumber {get; private set;}    
}

Maintenant, quand vous catch(ParserException x) vous aurez une sémantique supplémentaire pour affiner la gestion des exceptions.

Les classes personnalisées peuvent implémenter les fonctionnalités suivantes pour prendre en charge des scénarios supplémentaires.

lancer à nouveau

Pendant le processus d'analyse, l'exception d'origine est toujours intéressante. Dans cet exemple, il s'agit d'une FormatException car le code tente d'analyser un morceau de chaîne, qui devrait être un nombre. Dans ce cas, l'exception personnalisée devrait prendre en charge l'inclusion de « InnerException »:

//new constructor:
ParserException(string msg, Exception inner) : base(msg, inner) {
}

sérialisation

Dans certains cas, vos exceptions peuvent avoir à franchir les limites de AppDomain. C'est le cas si votre analyseur s'exécute dans son propre AppDomain pour prendre en charge le rechargement à chaud de nouvelles configurations d'analyseur. Dans Visual Studio, vous pouvez utiliser le modèle Exception pour générer du code comme celui-ci.

[Serializable]
public class ParserException : Exception
{
    // Constructor without arguments allows throwing your exception without
    // providing any information, including error message. Should be included
    // if your exception is meaningful without any additional details. Should
    // set message by calling base constructor (default message is not helpful).
    public ParserException()
        : base("Parser failure.")
    {}

    // Constructor with message argument allows overriding default error message.
    // Should be included if users can provide more helpful messages than
    // generic automatically generated messages.
    public ParserException(string message) 
        : base(message)
    {}

    // Constructor for serialization support. If your exception contains custom
    // properties, read their values here.
    protected ParserException(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {}
}

Utiliser l'Exception Parser

try
{
    Process.StartRun(fileName)
}
catch (ParserException ex)
{
    Console.WriteLine($"{ex.Message} in ${ex.FileName}:${ex.LineNumber}");
}
catch (PostProcessException x) 
{
    ...
}

Vous pouvez également utiliser des exceptions personnalisées pour intercepter et encapsuler des exceptions. De cette façon, de nombreuses erreurs différentes peuvent être converties en un seul type d'erreur plus utile pour l'application:

try
{
    int foo = int.Parse(token);
}
catch (FormatException ex)
{
    //Assuming you added this constructor
    throw new ParserException(
      $"Failed to read {token} as number.", 
      FileName, 
      LineNumber, 
      ex);
}

Lorsque vous gérez des exceptions en levant vos propres exceptions personnalisées, vous devez généralement inclure une référence à l'exception d'origine dans la InnerException , comme indiqué ci-dessus.

Problèmes de sécurité

Si exposer la raison de l'exception peut compromettre la sécurité en permettant aux utilisateurs de voir le fonctionnement interne de votre application, il peut être une mauvaise idée d'envelopper l'exception interne. Cela peut s'appliquer si vous créez une bibliothèque de classes qui sera utilisée par d'autres.

Voici comment vous pouvez générer une exception personnalisée sans encapsuler l'exception interne:

try
{
  // ...
}
catch (SomeStandardException ex)
{
  // ...
  throw new MyCustomException(someMessage);
}

Conclusion

Lorsque vous déclenchez une exception personnalisée (avec une nouvelle enveloppe ou une nouvelle exception non emballée), vous devez générer une exception significative pour l'appelant. Par exemple, un utilisateur d'une bibliothèque de classes peut ne pas savoir comment cette bibliothèque exécute son travail interne. Les exceptions levées par les dépendances de la bibliothèque de classes ne sont pas significatives. Au contraire, l'utilisateur souhaite une exception qui concerne la manière dont la bibliothèque de classes utilise ces dépendances de manière erronée.

try
{
  // ...
}
catch (IOException ex)
{
  // ...
  throw new StorageServiceException(@"The Storage Service encountered a problem saving
your data. Please consult the inner exception for technical details. 
If you are not able to resolve the problem, please call 555-555-1234 for technical       
assistance.", ex);
}