• Aucun résultat trouvé

2 Classes, Stuctures, Interfaces

1. public class C : IEnumerator{ 2

2.9 Les espaces de noms

Pour écrire une ligne à l'écran, nous utilisons l'instruction Console.WriteLine(...)

Si nous regardons la définition de la classe Console

Namespace: System

Assembly: Mscorlib (in Mscorlib.dll)

on découvre qu'elle fait partie de l'espace de noms System. Cela signifie que la classe Console devrait être désignée par System.Console et on devrait en fait écrire :

System.Console.WriteLine(...) On évite cela en utilisant une clause using :

using System; ...

Console.WriteLine(...)

On dit qu'on importe l'espace de noms System avec la clause using. Lorsque le compilateur va rencontrer le nom d'une classe (ici

Console) il va chercher à la trouver dans les différents espaces de noms importés par les clauses using. Ici il trouvera la classe Console

dans l'espace de noms System. Notons maintenant la seconde information attachée à la classe Console :

Assembly: Mscorlib (in Mscorlib.dll)

Cette ligne indique dans quelle "assemblage" se trouve la définition de la classe Console. Lorsqu'on compile en-dehors de Visual Studio et qu'on doit donner les références des différentes dll contenant les classes que l'on doit utiliser, cette information peut s'avérer utile. Pour référencer les dll nécessaires à la compilation d'une classe, on écrit :

csc /r:fic1.dll /r:fic2.dll ... prog.cs

où csc est le compilateur C#. Lorsqu'on crée une classe, on peut la créer à l'intérieur d'un espace de noms. Le but de ces espaces de noms est d'éviter les conflits de noms entre classes lorsque celles-ci sont vendues par exemple. Considérons deux entreprises E1 et E2 distribuant des classes empaquetées respectivement dans les dll, e1.dll et e2.dll. Soit un client C qui achète ces deux ensembles de classes dans lesquelles les deux entreprises ont défini toutes deux une classe Personne. Le client C compile un programme de la façon suivante :

Si le source prog.cs utilise la classe Personne, le compilateur ne saura pas s'il doit prendre la classe Personne de e1.dll ou celle de e2.dll. Il signalera une erreur. Si l'entreprise E1 prend soin de créer ses classes dans un espace de noms appelé E1 et l'entreprise E2 dans un espace de noms appelé E2, les deux classes Personne s'appelleront alors E1.Personne et E2.Personne. Le client devra employer dans ses classes soit E1.Personne, soit E2.Personne mais pas Personne. L'espace de noms permet de lever l'ambiguïté.

Pour créer une classe dans un espace de noms, on écrit : namespace EspaceDeNoms{

// définition de la classe }

2.10 Application exemple - V2

On reprend le calcul de l'impôt déjà étudié dans le chapitre précédent page 31 et on le traite maintenant en utilisant des classes et des interfaces. Rappelons le problème :

On se propose d'écrire un programme permettant de calculer l'impôt d'un contribuable. On se place dans le cas simplifié d'un contribuable n'ayant que son seul salaire à déclarer (chiffres 2004 pour revenus 2003) :

on calcule le nombre de parts du salarié nbParts=nbEnfants/2 +1 s'il n'est pas marié, nbEnfants/2+2 s'il est marié, où

nbEnfants est son nombre d'enfants.

• s'il a au moins trois enfants, il a une demi part de plus

on calcule son revenu imposable R=0.72*S où S est son salaire annuelon calcule son coefficient familial QF=R/nbParts

on calcule son impôt I. Considérons le tableau suivant :

4262 0 0 8382 0.0683 291.09 14753 0.1914 1322.92 23888 0.2826 2668.39 38868 0.3738 4846.98 47932 0.4262 6883.66 0 0.4809 9505.54

Chaque ligne a 3 champs. Pour calculer l'impôt I, on recherche la première ligne où QF<=champ1. Par exemple, si QF=5000 on trouvera la ligne

8382 0.0683 291.09

L'impôt I est alors égal à 0.0683*R - 291.09*nbParts. Si QF est tel que la relation QF<=champ1 n'est jamais vérifiée, alors ce sont les coefficients de la dernière ligne qui sont utilisés. Ici :

0 0.4809 9505.54

ce qui donne l'impôt I=0.4809*R - 9505.54*nbParts.

Tout d'abord, nous définissons une structure capable d'encapsuler une ligne du tableau précédent : 1. namespace Chap2 {

2. // une tranche d'impôt 3. struct TrancheImpot {

4. public decimal Limite { get; set; } 5. public decimal CoeffR { get; set; } 6. public decimal CoeffN { get; set; } 7. }

8. } 9.

Puis nous définissons une interface IImpot capable de calculer l'impôt : 1. namespace Chap2 {

2. interface IImpot {

3. int calculer(bool marié, int nbEnfants, int salaire); 4. }

5. }

• ligne 3 : la méthode de calcul de l'impôt à partir de trois données : l'état marié ou non du contribuable, son nombre d'enfants, son salaire

Ensuite, nous définissons une classe abstraite implémentant cette interface : 1. namespace Chap2 {

2. abstract class AbstractImpot : IImpot { 3.

4. // les tranches d'impôt nécessaires au calcul de l'impôt 5. // proviennent d'une source extérieure

6.

7. protected TrancheImpot[] tranchesImpot; 8.

9. // calcul de l'impôt

10. public int calculer(bool marié, int nbEnfants, int salaire) { 11. // calcul du nombre de parts

12. decimal nbParts;

13. if (marié) nbParts = (decimal)nbEnfants / 2 + 2; 14. else nbParts = (decimal)nbEnfants / 2 + 1; 15. if (nbEnfants >= 3) nbParts += 0.5M;

16. // calcul revenu imposable & Quotient familial 17. decimal revenu = 0.72M * salaire;

18. decimal QF = revenu / nbParts; 19. // calcul de l'impôt

20. tranchesImpot[tranchesImpot.Length - 1].Limite = QF + 1; 21. int i = 0;

22. while (QF > tranchesImpot[i].Limite) i++; 23. // retour résultat

24. return (int)(revenu * tranchesImpot[i].CoeffR - nbParts * tranchesImpot[i].CoeffN); 25. }//calculer

26. }//classe 27.

28. }

ligne 2 : la classe AbstractImpot implémente l'interface IImpot.

ligne 7 : les données annuelles du calcul de l'impôt sous forme d'un champ protégé. La classe AbstractImpot ne sait pas comment sera initialisé ce champ. Elle en laisse le soin aux classes dérivées. C'est pourquoi elle est déclarée abstraite (ligne 2) afin d'en interdire toute instanciation.

lignes 10-25 : l'implémentation de la méthode calculer de l'interface IImpot. Les classes dérivées n'auront pas à réécrire cette méthode. La classe AbstractImpot sert ainsi de classe de factorisation des classes dérivées. On y met ce qui est commun à toutes les classes dérivées.

Une classe implémentant l'interface IImpot peut être construite en dérivant la classe AbstractImpot. C'est ce que nous faisons maintenant :

1. using System; 2.

3. namespace Chap2 {

4. class HardwiredImpot : AbstractImpot { 5.

6. // tableaux de données nécessaires au calcul de l'impôt

7. decimal[] limites = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M }; 8. decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };

9. decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M }; 10.

11. public HardwiredImpot() {

12. // création du tableau des tranches d'impôt 13. tranchesImpot = new TrancheImpot[limites.Length]; 14. // remplissage

15. for (int i = 0; i < tranchesImpot.Length; i++) {

16. tranchesImpot[i] = new TrancheImpot { Limite = limites[i], CoeffR = coeffR[i], CoeffN = coeffN[i] };

17. }

18. }

19. }// classe 20. }// namespace

La classe HardwiredImpot définit, lignes 7-9, en dur les données nécessaires au calcul de l'impôt. Son constructeur (lignes 11-18) utilise ces données pour initialiser le champ protégé tranchesImpot de la classe mère AbstractImpot.

Un programme de test pourait être le suivant : 1. using System;

2.

4. class Program {

5. static void Main() {

6. // programme interactif de calcul d'Impot

7. // l'utilisateur tape trois données au clavier : marié nbEnfants salaire 8. // le programme affiche alors l'Impot à payer

9.

10. const string syntaxe = "syntaxe : Marié NbEnfants Salaire\n"

11. + "Marié : o pour marié, n pour non marié\n"

12. + "NbEnfants : nombre d'enfants\n"

13. + "Salaire : salaire annuel en F"; 14.

15. // création d'un objet IImpot 16. IImpot impot = new HardwiredImpot(); 17.

18. // boucle infinie 19. while (true) {

20. // on demande les paramètres du calcul de l'impôt

21. Console.Write("Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :");

22. string paramètres = Console.ReadLine().Trim(); 23. // qq chose à faire ?

24. if (paramètres == null || paramètres == "") break;

25. // vérification du nombre d'arguments dans la ligne saisie 26. string[] args = paramètres.Split(null);

27. int nbParamètres = args.Length; 28. if (nbParamètres != 3) {

29. Console.WriteLine(syntaxe);

30. continue;

31. }//if

32. // vérification de la validité des paramètres

33. // marié

34. string marié = args[0].ToLower(); 35. if (marié != "o" && marié != "n") {

36. Console.WriteLine(syntaxe + "\nArgument marié incorrect : tapez o ou n"); 37. continue;

38. }//if

39. // nbEnfants

40. int nbEnfants = 0; 41. bool dataOk = false;

42. try {

43. nbEnfants = int.Parse(args[1]); 44. dataOk = nbEnfants >= 0; 45. } catch {

46. }//if

47. // donnée correcte ? 48. if (!dataOk) {

49. Console.WriteLine(syntaxe + "\nArgument NbEnfants incorrect : tapez un entier positif ou nul"); 50. continue; 51. } 52. // salaire 53. int salaire = 0; 54. dataOk = false; 55. try {

56. salaire = int.Parse(args[2]); 57. dataOk = salaire >= 0;

58. } catch {

59. }//try-catch

60. // donnée correcte ? 61. if (!dataOk) {

62. Console.WriteLine(syntaxe + "\nArgument salaire incorrect : tapez un entier positif ou nul");

63. continue;

64. }

65. // les paramètres sont corrects - on calcule l'Impot

66. Console.WriteLine("Impot=" + impot.calculer(marié == "o", nbEnfants, salaire) + " euros"); 67. // contribuable suivant 68. }//while 69. } 70. } 71. }

ligne 16 : création d'un objet impot implémentant l'interface IImpot. Cet objet est obtenu par instanciation d'un type

HardwiredImpot, un type qui implémente l'interface IImpot. On notera qu'on n'a pas donné à la variable impot, le type HardwiredImpot mais le type IImpot. En écrivant cela, on indique qu'on ne s'intéresse qu'à la méthode calculer de l'objet impot

et pas au reste.

• lignes 19-68 : la boucle des simulations de calcul de l'impôt

ligne 22 : les trois paramètres nécessaires à la méthode calculer sont demandés en une seule ligne tapée au clavier.

• ligne 26 : la méthode [chaine].Split(null) permet de décomposer [chaine] en mots. Ceux-ci sont stockés dans un tableau

args.

ligne 66 : appel de la méthode calculer de l'objet impot implémentant l'interface IImpot. Voici un exemple d'exécution du programme :

1. Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :q s d

2. syntaxe : Marié NbEnfants Salaire 3. Marié : o pour marié, n pour non marié 4. NbEnfants : nombre d'enfants

5. Salaire : salaire annuel en euros 6. Argument marié incorrect : tapez o ou n

7. Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :o 2 d

8. syntaxe : Marié NbEnfants Salaire 9. Marié : o pour marié, n pour non marié 10. NbEnfants : nombre d'enfants

11. Salaire : salaire annuel en euros

12. Argument salaire incorrect : tapez un entier positif ou nul

13. Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :q s d f

14. syntaxe : Marié NbEnfants Salaire 15. Marié : o pour marié, n pour non marié 16. NbEnfants : nombre d'enfants

17. Salaire : salaire annuel en euros

18. Paramètres du calcul de l'Impot au format : Marié (o/n) NbEnfants Salaire ou rien pour arrêter :o 2 60000