DY

09 Les exceptions

Exceptions prédéfinies

Il existe de nombreuses exceptions prédéfinies en C#:

Intercepter une exception

Prenons ce code source:

string user_value = "vingt";
int value = int.Parse(user_value);

Si nous l'exécutons nous obtenons l'erreur suivante:

Unhandled Exception: System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(ReadOnlySpan`1 str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(ReadOnlySpan`1 s, NumberStyles style, NumberFormatInfo info)
   at System.Int32.Parse(String s)
   at exceptions.Program.Main(String[] args) in /home/sam/Documents/IUT_Projects/dotnet_A1/base_csharp/exceptions/Program.cs:line 13

L’application « plante » lamentablement produisant un rapport d’erreur. Lors de la conversion, si le framework .NET n’arrive pas à convertir correctement la chaine de caractères en entier, il lève une exception. Cela veut dire qu’il alerte le programme qu’il rencontre un cas en erreur qui nécessite d’être géré.

Si ce cas n’est pas géré, alors l’application plante et c’est le CLR qui intercepte l’erreur et qui fait produire un rapport par le Framework.

Pourquoi une exception et pas un code d’erreur ?

L’intérêt des exceptions est qu'elles sont typées. Finis les codes d’erreurs incompréhensibles. C’est le type de l’exception, c'est-à-dire sa classe, qui va nous permettre d’identifier le problème.

Pour éviter une mort douloureuse à notre application, nous devons gérer ces cas et intercepter les exceptions.

Pour ce faire, il faut encadrer les instructions à risque avec le bloc d’instruction try catch, par exemple :

try
{
    string user_value = "vingt";
    int value = int.Parse(user_value);
    Console.WriteLine("This line will never be executed");
}
catch (Exception)
{
    Console.WriteLine("Error during user value conversion.");
}

Nous avons « attrapé » l’exception levée par la méthode de conversion grâce au mot-clé catch.

Cette structure nous permet de surveiller l’exécution d’un bout de code, situé dans le bloc try et s’il y a une erreur, alors nous interrompons son exécution pour traiter l’erreur dans le bloc catch.

La suite du code dans le try, à savoir l’affichage de la ligne avec Console.WriteLine, ne sera jamais exécuté car lorsque la conversion échoue, il saute directement au bloc catch.

Inversement, il est possible de ne jamais passer dans le bloc catch si les instructions ne provoquent pas d’erreur. Essayer donc de remplacer vingt par 20. Que se passe t'il ?

Lever une exception

Il est possible de déclencher soi-même la levée d’une exception. C’est utile si nous considérons que notre code a atteint un cas d'eeruer, qu’il soit fonctionnel ou technique.

Pour lever une exception, nous utilisons le mot-clé throw, suivi d’une instance d’une exception.

Imaginons par exemple une méthode permettant de calculer la racine carrée d’un double.

Nous pouvons obtenir un cas d'erreur lorsque nous tentons de passer un double négatif :

public static double RacineCarree(double valeur)
{
    if (valeur <= 0)
        throw new ArgumentOutOfRangeException("valeur", "Le paramètre doit être positif");
    return Math.Sqrt(valeur);
}

Essayez maintenant d'exécuter la méthode RacineCarree en lui passant en paramètre -42. Que se passe t'il ?

Exemple 1 - Division

Voici le code source de la classe MultipleDivision qui prend en paramètre de constructeur une liste d'entier et posséde une méthode division qui permet de diviser un élément de la liste par un autre.

UML class diagram
Diagramme UML de la classe MultipleDivision

et voici le code source de cette classe:

using System.Collections.Generic;

namespace MyMath 
{
    public class MultipleDivision
    {
        private List<int> values;
        public MultipleDivision(List<int> values)
        {
            this.values = values;
        }

        public Division(int index_dividende, int index_diviseur)
        {
            double quotient;
            quotient = this.values[index_dividende] / this.values[index_diviseur];
            return quotient;
        }
    }
}

Puis dans notre main:

List<int> values = new List<int>{17, 12, 15, 38, 29, 157, 89, -22, 0, 5};
MultipleDivision mltd = new MultipleDivision(values);

//TODO: Ajouter saisie index_dividende, puis saisie index_diviseur
//TODO: Call mltd.division(index_dividende, index_diviseur)
//TODO: Show Result

Exercice 2 - Menu

Il s'agit dans cet exercie de faire une série de choix pour un menu sécurisé.

Il peut être utile de faire l'analyse et le diagramme UML de cet exercice avant de vous lancer dans l'implémentation.

Levée d'exception

Nous allons créer une méthode inputChoice qui prendra en paramètre un entier n ainsi qu'une valeur max et renverra une valeur comprise entre 1 et n saisie au clavier par l’utilisateur.

Les différentes erreurs qui pourrontse produire seront:

Chaque erreur devra être détectée par le programme et être signalée par une exception spécifique. Il faut donc créer trois classes différentes d’exception. Pour simplifier, vous pouvez vous limiter à max < 10.

Affichage du menu
Question et réponse
Classe