09 Les exceptions
Exceptions prédéfinies
Il existe de nombreuses exceptions prédéfinies en C#:
DivideByZeroException
: Exception levée pendant une tentative de division par zéro d'une valeur intégrale ou Decimal;FormatException
: Exception levée quand le format d’un argument n’est pas valide ou qu’une chaîne de format composite n’est pas formée correctement;ArithmeticException
: Exception levée quand des erreurs se produisent dans une opération arithmétique, de transtypage ou de conversion;NotImplementedException
: Exception levée lorsqu'une méthode ou une opération demandée n'est pas implémentée;Exception
: Représente les erreurs qui se produisent lors de l'exécution de l'application.
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.
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
- Modifiez le
main
pour implémenter la saisie du dividende et du diviseur; - Essayez votre programme avec les index 1 et 2, puis 3 et 4 et enfin 9 et 8;
- Que se passe t'il ?
- Pouvez vous corriger cette erreur ?
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:
n
est inférieur ou égal à 1;- l’utilisateur a entré un nombre qui n’est pas compris entre 1 et
max
; - l’utilisateur n’a pas entré un entier.
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
-
Écrire une méthode qui prend en paramètre une liste de chaînes de caractères.
-
Chaque chaîne de caractères décrit un choix du menu. La méthode doit afficher ces différents choix.
Question et réponse
- Écrire une méthode qui utilise les deux méthodes déjà écrites pour afficher un menu et saisir le choix de l’utilisateur.
- Cette méthode devra gérer les trois exceptions définies à la question 1.
- Elle prendra en entrée une liste d’options (string) et en sortie le choix effectué (sous la forme d’un entier).
Classe
- Écrivez une classe
Menu
qui possède un constructeur pour initialiser une liste de chaînes représentant différents choix ainsi que la question auquel vous devez répondre. - Cette classe permettra de réaliser une saisie d’un des choix au moyen d’un entier, en utilisant les exception nécessaires.
- Cette classe reprendra les fonctionnalités développées aux questions précédentes, mais adaptées à la structure de classe.
- Écrivez un programme qui utilise cette classe
Menu
avec trois menus différents qui permettront d'accèder à trois fonctionnalités différentes (simples affichage d'une citation par exemple). - Ce programme capturera les exceptions susceptibles d’être levées.