IFT 1179 : Programmation en C#
A) Classe abstraite (abstract class):
Pourquoi ?
Lors de la conception d'une hiérarchie, on a besoin de créer des classes plus générales d'abord et différer l'implémentation à des étapes plus éloignées quand le comportement est très bien défini. Quand on parle de figure géométrique, il est trop vague pour savoir comment calculer un périmètre, une surface : ces méthodes sont encore abstraites. Il est impossible de réaliser (implémenter) ces méthodes. Par contre, un rectangle est une figure géométrique dont le calcul du périmètre et de la surface est connu, on peut alors implémenter ces méthodes qui sont très concrètes (pas du tout abstraites) pour un rectangle.
classe abstraite : Figure géométrique peut-on calculer :
1) le périmètre de n'importe quelle figure ? non! => ce calcul est abstrait!
2) la surface de n'importe quelle figure ? non! => ce calcul est abstrait!
classe
Parallelogramme
périmètre : Oui
2x(cote1+cote2)
surface : Non encore abstraite
classe Cercle périmètre : Oui
2xMath.Pi x rayon
surface : Oui Math.Pi x rayon
2classe Triangle périmètre : Oui
côté1+côté2+côté3
surface : Non
classe Rectangle
surface : Oui
longueur x largeur classe concrète
classe
TriangleRectangle
surface : Oui
hauteur x base / 2 classe concrète
En résumé : une classe abstraite est celle qui n'est que partiellement implantée
using System;
abstract class FigureGeo {
public abstract double Perimetre();public abstract double Surface();
public override string ToString(){
return string.Format("\nPerimetre : {0, 7:F2}, Surface : {1, 7:F2}", Perimetre(),Surface());
}
}
/* Cette classe est CONCRÈTE car toutes les méthodes sont implénentées (réalisables)
*/
class Cercle : FigureGeo {
private double rayon;
public Cercle(double rayon) { this.rayon = rayon;
}
public override double Perimetre() { return 2 * Math.PI * rayon;
}
public override double Surface() { return Math.PI * rayon * rayon;
}
public override string ToString() {
return "Cercle de rayon : " + rayon +
base.ToString();
}
}
/* Cette classe est encore abstraite car on ne sait pas encore comment calculer la Surface.
*/
abstract class Parallelogramme: FigureGeo {
protected double cote1, cote2;
// on a quand même le droit d'avoir de constructeur public Parallelogramme(double cote1, double cote2) {
this.cote1 = cote1;
this.cote2 = cote2;
}
// cette méthode est "concrète" ici:
public override double Perimetre() { return 2 * (cote1+cote2);
}
public override string ToString() {
return "Les 2 cotes : " + cote1 + ", " + cote2 + "\n" + base.ToString();
}
}
/* Cette classe est CONCRÈTE car toutes les méthodes sont implémentées (réalisables)
*/
class Rectangle
: Parallelogramme {
public Rectangle(double longueur, double largeur) : base(longueur, largeur)
{ }
public override double Surface() { return cote1*cote2;
}
public override string ToString() {
return "En tant qu'un rectangle : " + base.ToString();
} }
class Abstraite1 {
static void Main (string[] args) {
Cercle c = new Cercle(10.0);
Console.WriteLine(c);
Rectangle r = new Rectangle(6.8, 3.2);
Console.WriteLine("Infos du rectangle r :\n" + r);
} }
/* Exécution:
Cercle de rayon : 10
Perimetre : 62,83, Surface : 314,16 Infos du rectangle r :
En tant qu'un rectangle : Les 2 cotes : 6,8, 3,2
Perimetre : 20,00, Surface : 21,76 Press any key to continue
*/
Exercices :
Programmez la classe abstraite Triangle et sa classe dérivée, concrète TriangleRectangle .
Remarques :
1. On n’a pas le droit d’instancier (avec new) un objet d’une classe abstraite;
2. Une classe abstraite peut contenir des méthodes non abstraites;
3. Une classe abstraite peut contenir des constructeurs pour faire appeler par ses classes dérivées ( avec base(. . .) )
4. Une méthode abstraite est automatiquement virtuelle (mais on n’a pas le droit de fournir ce mot virtual sur l’en-tête). Ces méthodes
abstraites peuvent être toutes ou en partie redéfinies (override) dans une classe dérivée.
Réflexions :
Dans tous les jeux de cartes, on devrait (entre autres) distribuer des cartes. La méthode distribuer varie d’un jeux de cartes à un autre jeux.
Cette méthode est donc abstraite. La classe JeuxDeCartes est alors une
classe abstraite.
B) Interface :
Pourquoi ?
Le C# (et aussi Java) ne supporte pas l'héritage multiple comme C++ :
Classe de base
A1
Classe de base
A2
Classe de base
A3
etc …
classe dérivée en C++
B
En C# une classe n'a pas le droit d'hériter directement deux classes de base. Par contre, C# permet à une classe d'être dérivée d'une seule classe de base mais implémente une ou plusieurs interfaces.
Classe de base en
C#
A
interface I1
interface
I2 etc
sous-classe B dérivée de A implémente I1, I2, etc
De plus, une interface contient souvent ses méthodes abstraites qui seront réalisées dans
les classes qui implémentent cette interface.
Premier exemple :
/* Fichier Interface1.cs
* Premier exemple d'une interface (ici IFigure) */
using System;
// cette interface comporte deux méthodes à implémenter // dans Cercle, Carre, ...
interface IFigure
{ double Perimetre();
double Surface();
}
// la classe Cercle IMPLÉMENTE l'interface IFigure
class Cercle: IFigure {
private double rayon;public Cercle(double rayon) { this.rayon = rayon;
}
public Cercle() {
}
public double Perimetre() { return 2 * Math.PI * rayon;
}
public double Surface() {
return Math.PI * rayon * rayon;
}
}
class Carre : IFigure {
private double cote;public Carre(double cote) { this.cote = cote;
}
public Carre() {
}
public double Perimetre() { return 4 * cote;
}
public double Surface() { return cote*cote;
}
}
class Interface1 {
static void Main(string[] args) {
IFigure[] fig = { new Cercle(10.2),
new Carre(2.8), new Carre(6.5) };
double somme = 0;
foreach (IFigure figure in fig)
somme += figure.Surface();
Console.WriteLine("Surface totale {0, 8:F2}", somme);
}
}
/* Execution:
Surface totale 376,94 Press any key to continue
*/
Exemple 2 : Peut-on trier n’importe quel tableau avec une même méthode de tri ?
using System;
class Personne : IComparable {
protected string nomPre ;
protected double taille, poids;
protected char sexe;
public Personne(string nomPre, double taille, double poids, char sexe)
{
this.nomPre = nomPre;
this.taille = taille;
this.poids = poids;
this.sexe = sexe;
}
public Personne(string nomPre, double taille, double poids) : this (nomPre, taille, poids, 'F')
{ }
public Personne() {
}
public override string ToString() {
return string.Format("{0,-20:S} {1,6:F2} {2, 6:F2}
{3,10:S}", nomPre, taille, poids, (sexe == 'F' ? "feminin":"masculin"));
}
public int CompareTo(object obj) {
Personne autre = (Personne) obj;
return nomPre.ToUpper().CompareTo( autre.nomPre.ToUpper() ) ; }
}
class Station : IComparable {
private string nom; // nom de la station private int rang2003, // rang en 2003
freq2004; // nb. de passagers aux tourniquets en 2004
// un constructeur possible public Station(string nom, int rang, int freq2004) {
this.nom = nom;
rang2003 = rang;
this.freq2004 = freq2004;
}
// un autre constructeur possible public Station(string chaine)
{
char[] separateurs = new char[] {'(', ')'};
string[] infosMetro = chaine.Split(separateurs, 3);
nom = infosMetro[0];
rang2003 = int.Parse(infosMetro[1]);
freq2004 = int.Parse(infosMetro[2]);
}
// un constructeur sans paramètre public Station()
{ }
// redéfinition ToString pour afficher une station public override string ToString()
{
return String.Format("{0, -35:S} {1,4:D} {2, 12:N0}", nom, rang2003, freq2004 );
}
public int CompareTo(object obj) {
Station autre = (Station) obj;
return freq2004 - autre.freq2004;
} }
class Interface2 {
static void Permuter(IComparable[] tableau, int i, int indMin) {
IComparable tempo = tableau[i];
tableau[i] = tableau[indMin];
tableau[indMin] = tempo;
}
static void Trier(IComparable[] tableau, int nbElem) {
for(int i = 0; i < nbElem-1; i++) {
int indMin = i;
for (int j = i+1; j < nbElem ; j++)
if (tableau[j].CompareTo(tableau[indMin]) < 0)
indMin = j;
if (indMin != i)
}
static void Afficher(IComparable[] tableau, string mess) {
Console.WriteLine("\nContenu du tableau des " + mess + ":\n");
for (int i = 0; i < tableau.Length; i++)
Console.WriteLine("{0,3:D}) {1}", i, tableau[i]);
Console.WriteLine();
}
static void Main(string[] args) {
Personne[] pers = {
new Personne("Tremblay Jacques", 1.72, 78.9, 'M'), new Personne("Charbonneau Lise", 1.83, 65.1, 'F'), new Personne("Ferland Pierre", 1.58, 80.7, 'M')} ;
Trier(pers, pers.Length);
Afficher(pers, "personnes apres le tri selon les noms");
Station[] stat = {
new Station("COTE-VERTU(9)6387345"), new Station("DE LA SAVANE(65)690673"), new Station("JEAN-TALON(11)5021175"),
new Station("COTE-DES-NEIGES(26)3509564") };
Trier(stat, stat.Length);
Afficher(stat, "stations apres le tri selon la frequence en 2004");
} }
/* Exécution:
*
Contenu du tableau des personnes apres le tri selon les noms:
0) Charbonneau Lise 1,83 65,10 feminin 1) Ferland Pierre 1,58 80,70 masculin 2) Tremblay Jacques 1,72 78,90 masculin
Contenu du tableau des stations apres le tri selon la frequence en 2004:
0) DE LA SAVANE 65 690 673 1) COTE-DES-NEIGES 26 3 509 564 2) JEAN-TALON 11 5 021 175 3) COTE-VERTU 9 6 387 345 Press any key to continue
*/
Exemple 3 : Devrait-on toujours écrire des méthodes pour le tri et la recherche dans un tableau trié ?
/* Fichier Interface3.cs */
using System;
using System.IO;
class Station : IComparable {
private string nom; // nom de la station private int rang2003, // rang en 2003
freq2004; // nb. de passagers aux tourniquets en 2004
// un constructeur possible
public Station(string nom, int rang, int freq2004) {
this.nom = nom.ToUpper();
rang2003 = rang;
this.freq2004 = freq2004;
}
// un autre constructeur possible public Station(string chaine)
{
char[] separateurs = new char[] {'(', ')'};
string[] infosMetro = chaine.Split(separateurs, 3);
nom = infosMetro[0];
rang2003 = int.Parse(infosMetro[1]);
freq2004 = int.Parse(infosMetro[2]);
}
// un constructeur sans paramètre public Station()
{ }
// pour afficher les infos d'une station public override string ToString()
{
return string.Format("{0, -35:S} {1,4:D} {2, 12:N0}", nom, rang2003, freq2004 );
}
public int CompareTo(object obj) {
return nom.ToUpper().CompareTo( ((Station) obj).nom.ToUpper() ) ;
}
}
class Interface3 {
static void LireRemplir(string nomFichier, Station[] stat, out int n)
{
n = 0; // compteur du nombre de dtations lues
StreamReader aLire = File.OpenText(nomFichier);
string ligneLue = null;
while ( (ligneLue = aLire.ReadLine()) != null) stat[n++] = new Station(ligneLue);
aLire.Close();
Console.WriteLine("\nOn vient de creer un tableau de "
+ n + " stations");
}
static void Afficher(Station[] stat, int nbStat, int debut, int fin, string message)
{
Console.WriteLine("\nAffichage partiel du tableau des stations " + message);
for (int i = 0; i < nbStat; i++)
if (i < debut || i >= nbStat - fin)
Console.WriteLine("{0,6:D}) {1}", i, stat[i]);
else if (i == debut+1)
Console.WriteLine("etc .... ");
Console.WriteLine();
}
static void Main(string[] args) {
const int MAX_STAT = 70; // au maximum, 70 stations Station[] stat = new Station[MAX_STAT];
int nbStat;
LireRemplir("R:\\metro.txt", stat, out nbStat);
Afficher(stat, nbStat, 7, 4, "avant le tri");
Array.Sort(stat, 0, nbStat);
Afficher(stat, nbStat, 3, 5,
"apres le tri avec Array.Sort");
int indice = Array.BinarySearch(stat, 0, nbStat, new Station("Guy-Concordia", 0, 0));
Console.WriteLine("A l'indice " + indice + ", on trouve :\n" + stat[indice]);
indice = Array.BinarySearch(stat, 0, nbStat, new Station("Laval", 0, 0));
if (indice < 0)
Console.WriteLine("On ne trouve pas Laval");
} } /* Exécution:
On vient de creer un tableau de 65 stations
Affichage partiel du tableau des stations avant le tri
0) MC-GILL 1 11 333 531 1) BERRI-UQAM 2 11 067 519 2) LONGUEUIL-UNIVERSITE-DE-SHERBROOKE 5 7 375 439 3) GUY-CONCORDIA 4 7 077 669 4) ATWATER 6 6 535 564 5) COTE-VERTU 9 6 387 345 6) PEEL 8 6 273 357 etc ....
61) ASSOMPTION 62 973 051 62) ACADIE 63 936 535 63) GEORGES-VANIER 64 695 482 64) DE LA SAVANE 65 690 673
Affichage partiel du tableau des stations apres le tri avec Array.Sort 0) ACADIE 63 936 535
1) ANGRIGNON 15 4 493 057 2) ASSOMPTION 62 973 051 etc ....
60) UNIVERSITE-DE-MONTREAL 38 2 610 922 61) VENDOME 12 4 889 583 62) VERDUN 51 1 529 707 63) VIAU 34 2 617 979 64) VILLA-MARIA 33 2 753 590
A l'indice 24, on trouve :
GUY-CONCORDIA 4 7 077 669 On ne trouve pas Laval
Press any key to continue
*/