polycopié - Rémy Malgouyres

Transcription

polycopié - Rémy Malgouyres
,
Conception Objet et Programmation en C#
Rémy Malgouyres
LIMOS UMR 6158, IUT, département info
Clermont Université
B.P. 86
63172 AUBIERE cedex
http ://www.malgouyres.org/
Table des matières
1 ABC de la conception et programmation objet
1.1 Conception du Package Métier . . . . . . . . . . . . . . . . . . . . . .
1.1.1 Organisation d’une Application en packages et/ou namespace
1.1.2 Dialogue avec l’Expert Métier . . . . . . . . . . . . . . . . . .
1.1.3 Déterminer les acteurs et actions simples . . . . . . . . . . . .
1.1.4 Première ébauche du package Métier . . . . . . . . . . . . . .
1.2 Créer une classe simple . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.1 Modélisation Statique (diagramme de classes) . . . . . . . . .
1.2.2 Code Source . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.3 Classe de test . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Relation de Composition . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.1 Modélisation Statique . . . . . . . . . . . . . . . . . . . . . .
1.3.2 Code source . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.3 Classe de tests . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4 Pattern Strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.1 Modélisation statique . . . . . . . . . . . . . . . . . . . . . .
1.4.2 Code source . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.3 Classe de Test . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5 Diagrammes de Séquence . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.1 Notion de diagramme de séquence et exemple simple . . . . .
1.5.2 Exemple de diagramme de séquence pour UrBookLTD . . . .
2 Delegates et Expressions Lambda
2.1 Fonctions de comparaison basiques . . . . . . .
2.2 Uniformisation de la signature des fonctions . .
2.3 Types delegate . . . . . . . . . . . . . . . . .
2.4 Exemple d’utilisation : méthode de tri générique
2.5 Expressions lambda . . . . . . . . . . . . . . . .
3 Collections
3.1 Listes . . . . . . . . . . .
3.2 Files . . . . . . . . . . . .
3.3 Dictionnaires . . . . . . .
3.4 Protocole de comparaison
3.5 Protocole d’égalité . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
3
3
4
4
5
5
5
6
8
8
8
12
12
13
13
15
17
17
20
.
.
.
.
.
22
22
25
27
30
31
.
.
.
.
.
33
34
35
36
38
39
TABLE DES MATIÈRES
4 Requêtes LINQ
4.1 Types délégués anonymes . . . . . . . . . .
4.2 Méthodes d’extension . . . . . . . . . . . . .
4.3 Interface IEnumerable<T> . . . . . . . . . .
4.4 Quelques méthodes de LINQ . . . . . . . . .
4.4.1 Filtre Where . . . . . . . . . . . . . .
4.4.2 Méthode de tri OrderBy . . . . . . .
4.4.3 Filtre Where sur un dictionnaire . . .
4.4.4 Méthode de regrouppement GroupBy
4.4.5 Exemples . . . . . . . . . . . . . . .
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
43
44
45
45
45
46
46
47
Chapitre 1
ABC de la conception et
programmation objet
1.1 Conception du Package Métier
1.1.1 Organisation d’une Application en packages et/ou namespace
Une application va regrouper plusieurs unités logiques, qui chacune comportent une ou (généralement) plusieurs classes. Une petite application peut comporter, par exemple :
• Le package Métier (qui dépend de l’activité du ”client” pour qui on développe l’application) ;
• Un ou plusieurs packages de mise en forme dans une Interface Homme Machine (IHM ) ;
• Un package de persistance (permet de gérer l’enregistrement des données de manière
permanente, par exemple dans une base de données) ;
• etc.
Nous développons dans la suite l’exemple d’une application pour un grossiste de
livres.
1.1.2 Dialogue avec l’Expert Métier
Supposons que nous développons une application de gestion pour un grossiste de livres Ur
Books LTD. Le but ici n’est pas de développer complètement cette aplication mais de donner
les grandes lignes de l’organisation de notre application, de la démarche, de la conception et
des outils de programmation.
a) Description du métier par l’expert
Nous intérogeons l’Expert Métier, c’est à dire la personne de Ur Books LTD avec qui nous
dialogons pour établir le cahier des charges ou valider le fonctionnement de notre logiciel. Cet
expert métier nous dit :
“Nous vendons des livres. Un livre possède un ou plusieurs auteurs et un éditeur,
plus éventuellement un numéro d’édition. Il y a deux formats de livres : les
3
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
formats poches et les formats brochés. Lorsqu’un client nous commande un (ou
plusieurs livres), nous devons, si le livre n’est pas en stock, commander nous même
des exemplaires du livre chez notre fournisseur. Lorsque nous avons reçu tous
les livres de la commande du client, nous envoyons les livres à l’adresse du client.
Le client peut être un professionnel (libraire) ou un particulier Si le client est un
professionnel, nous lui envoyons régulièrement automatiquement des informations
sur les nouveautés, qui nous sont données par les éditeurs.”
1.1.3 Déterminer les acteurs et actions simples
Toujours dans un dialogue avec l’expert métier et notre client, nous proposons ici une interface
de type ”console”. L’interface comporte différentes vues :
1. La vue client, correspondant à l’acteur Client. L’accueil propose les choix suivants :
• S’enregistrer ou modifier ses données personnelles (saisir son nom et ses coordonnées)
• Afficher la liste des livres du catalogue (avec pagination) ;
• Commander un livre en saisissant sa référence.
2. La vue du Back Office, correspondant à l’acteur Manager. L’accueil propose les choix
suivants :
• Consulter la liste des commandes et la liste des commandes en attente ;
• Voir l’état d’une commande et gérer une commande en saisissant son numéro de
commande.
1.1.4 Première ébauche du package Métier
Nous poposons de faire un package Métier qui contient :
• Une classe Livre ;
• Une classe Editeur ;
• Une classe Fournisseur ;
• Une classe Client ;
• Une classe Adresse pour les adresses des clients, des éditeurs et des fournisseurs ;
• Une classe Commande.
Ces classes sont destinées à représenter les entités manipulées dans le métier de notre client.
On commence par mettre en évidence les plus grosses classes. Nous pourrons toujours rajouter
des classes plus tard.
4
Chapitre 1 : ABC de la conception et programmation objet
1.2 Créer une classe simple
1.2.1 Modélisation Statique (diagramme de classes)
Voyons tout d’abord comment créer une classe pour représenter un numéro de téléphone.
Nous verrons ensuite comment les information concernant l’adresse d’une personne ou d’une
institution (entreprise ou administration) pourra “contenir” des numéros de téléphone.
La classe Telephone est immuable. Cela signifie
qu’une fois l’instance du téléphone créée, on ne
peut plus la modifier. Le seul moyen de changer
le numéro de téléphone serait de détruire (supprimer toute référence à) l’instance de la classe
Telephone correspondante, et de construire une
nouvelle instance.
Pour celà, comme tous les attributs sont eux
même immuables, il suffit d’interdire l’écriture
sur les attributs (attributs et leurs Setters privés).
Dans les setters, nous réaliserons des tests sur
la forme des chaînes de caractères pour assurer
la cohérence des données (et la sécurité de l’application). On utilise pour celà des expressions
régulières (Regex).
Dans la classe Adresse, nous avons cette fois
défini des propriétés, ce qui permet d’écrire les
Getters et les Setters de manière plus compacte.
Diag 1. Diagramme de Classes
La propriété Telephones, qui est de type Array
avec relation sous forme d’attribut.
de Telephone, est en lecture seule.
Cela ne signifie pas qu’une autre classe ne puisse pas modifier les numéros de
téléphone. En effet, l’accès à la référence de l’Array permet de modifier les éléments de cet Array, et donc par exemple d’ajouter ou supprimer un numéro de
téléphone. On voit donc que les Getters peuvent parfois permettre de modifier
les objets référencés. Ça ne serait pas le cas avec une structure, qui serait intégralement recopiée lors du return du Getter, interdisant la modification de ses
données internes.
1.2.2 Code Source
UrBooksLTD/Metier/Telephone.cs
1
2
3
4
5
6
7
8
9
using System ;
using System . Text ;
using System . Text . R e g u l a r E x p r e s s i o n s ;
namespace M e t i e r
{
public c l a s s Telephone
{
5
Rémy Malgouyres, www.malgouyres.org
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Conception Objet et Programmation en C#
private string m _ l i b e l l e ;
private string m_numero ;
public string G e t L i b e l l e ( )
{
return m _ l i b e l l e ;
}
public string GetNumero ( )
{
return m_numero ;
}
private void S e t L i b e l l e ( string l i b e l l e )
{
Regex myRegex = new Regex ( ” ^ [ a−zA−Z ] { 4 , 1 6 } $ ” ) ;
i f ( myRegex . IsMatch ( l i b e l l e ) )
{
m_libelle = l i b e l l e ;
}
else
{
throw new ArgumentException ( ”Le l i b e l l é e s t i n v a l i d e ” ) ;
}
}
private void SetNumero ( string numero )
{
Regex myRegex = new Regex ( ” ^[0 −9]{10} $ ” ) ;
i f ( myRegex . IsMatch ( numero ) )
{
m_numero = numero ;
}
else
{
throw new ArgumentException ( ”Le numéro e s t i n v a l i d e ” ) ;
}
}
public Telephone ( string l i b e l l e , string numero )
{
SetLibelle ( l i b e l l e ) ;
SetNumero ( numero ) ;
}
public override string To S tr i n g ( )
{
S t r i n g B u i l d e r r e t o u r = new S t r i n g B u i l d e r ( ) ;
r e t o u r . Append ( m _ l i b e l l e + ” : ” ) ;
r e t o u r . AppendLine ( ” ” + m_numero) ;
return r e t o u r . T o St r i n g ( ) ;
}
}
}
1.2.3 Classe de test
6
Chapitre 1 : ABC de la conception et programmation objet
UrBooksLTD/UrBooksLTD/TestMetierTelephone.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
using M e t i e r ;
namespace UrBooksLTD
{
class TestMetierTelephone : I T e s t I n t e r f a c e
{
public void Test ( )
{
C o n s o le . Write ( ” Merci de s a i s i r l e l i b e l l e : ” ) ;
string l i b e l l e = C on s o le . ReadLine ( ) ;
C o n s o le . Write ( ” Merci de s a i s i r l e numero : ” ) ;
string numero = C o n so l e . ReadLine ( ) ;
try
{
Telephone t e l = new Telephone ( l i b e l l e , numero ) ;
C on s o l e . Write ( t e l ) ;
}
catch ( E x c e p t i o n e )
{
C on s o l e . WriteLine ( ” Erreur
;
}
}
}
}
7
: ” + e . Message + ” \n” + e . StackTrace )
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
1.3 Relation de Composition
1.3.1 Modélisation Statique
Une autre représentation (pas tout à fait équivalente !) de la relation entre la classe Adresse
et la classe Telephone est la Composition.
On voit sur ce schéma, par le losange plein représentant une composition :
• que la classe Adresse va contenir un ensemble de Telephone.
• De plus, les cycles de vie sont liés.
Si une instance de la classe Adresse est
détruite, toutes les instances de la classe
Telephone correspondantes seront aussi
détruite.
• En particulier, une instance de Telephone
ne peut pas être partagée par plusieurs
instances de la classe Adresse.
Diag 2. Diagramme de Classes avec relation
Notons en outre que nous avons représenté les
sous forme de Composition
données de la classe téléphone sous forme de
propriétés enn lecture seule (typique de C#).
Notons enfin que nous avons défini la propriété Telephones de la classe adresse
en privé, interdisant ainsi aux autres classes d’accéder à la collection des téléphones. Cela garantit que la caractéristique de la composition que les
téléphones ne sont pas partagés ni modifiables par d’autres instance
que leur instance unique d’adresse. En contrepartie, on n’accède plus aux téléphones que sous forme de string pour affichage.
1.3.2 Code source
Voici le code C#de la classe Adresse :
UrBooksLTD/Metier/Adresse.cs
1
2
3
4
5
6
7
8
9
10
11
using System ;
using System . Text ;
using System . Text . R e g u l a r E x p r e s s i o n s ;
namespace M e t i e r
{
public c l a s s A d r e s s e
{
public A d r e s s e ( string numeroRue , string rue , string c o d e P o s t a l , string
ville )
{
8
Chapitre 1 : ABC de la conception et programmation objet
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
NumeroRue = numeroRue ;
Rue = r u e ;
CodePostal = c o d e P o s t a l ;
Ville = ville ;
T e l e p h on es = new Telephone [MAX_TELEPHONES ] ;
}
private s t a t i c void checkRegularExp ( string p a t t e r n , string c h a i n e ,
string errorMsg )
{
Regex myRegex = new Regex ( p a t t e r n ) ;
i f ( ! myRegex . IsMatch ( c h a i n e ) )
{
throw new ArgumentException ( ” Erreur : ” + errorMsg ) ;
}
}
private string m_numeroRue ;
public string NumeroRue
{
g e t { return m_numeroRue ; }
set
{
checkRegularExp (@” ^ [ a−zA−Z0−9\−\ ] { 0 , 1 6 } $ ” , value , ”Numéro de
rue i n v a l i d e ” ) ;
m_numeroRue = v a l u e ;
}
}
private string m_rue ;
public string Rue
{
g e t { return m_rue ; }
set
{
checkRegularExp (@” ^ [ \w\ s \−\ ]+$ ” , va lue , ”Nom de rue / p l a c e
invalide ”) ;
m_rue = v a l u e ;
}
}
private string m _ v i l l e ;
public string V i l l e
{
g e t { return m _ v i l l e ; }
set
{
checkRegularExp (@” ^ [ \w\ s \−\ ]+$ ” , va lue , ”Nom de v i l l e i n v a l i d e ”
);
m_ville = value ;
}
}
private string m_codePostal ;
public string CodePostal
{
g e t { return m_codePostal ; }
9
Rémy Malgouyres, www.malgouyres.org
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
Conception Objet et Programmation en C#
set
{
Regex myRegex = new Regex ( ” ^[0 −9]{5} $ ” ) ;
i f ( ! myRegex . IsMatch ( v a l u e ) )
{
throw new ArgumentException ( ”Code P o s t a l i n v a l i d e ” ) ;
}
m_codePostal = v a l u e ;
}
}
// Nombre Maximum de Numéros de T é l é p h o n e s
private s t a t i c readonly int MAX_TELEPHONES = 1 0 ;
// p r o p r i é t é en l e c t u r e s e u l e
private Telephone [ ] m_telephones ;
public Telephone [ ] Te l ep ho n e s
{
get
{
return m_telephones ;
}
// s e t t e r p r i v é ! ! !
private s e t
{
m_telephones = v a l u e ;
}
}
// / <summary>
// / A j o u t e un t é l é p h o n e
// / </summary>
// / <r e t u r n s >r e n v o i e t r u e s i i l r e s t e de l a p l a c e </r e t u r n s >
public bool AddTelephone ( string l i b e l l e , string numero )
{
int i n d e x A j o u t = Array . IndexOf ( Telephones , null ) ;
i f ( i n d e x A j o u t < MAX_TELEPHONES) {
Te l e p h o n e s [ i n d e x A j o u t ] = new Telephone ( l i b e l l e , numero ) ;
return true ;
} else {
return f a l s e ;
}
}
public bool RemoveTelephone ( string l i b e l l e )
{
// On c h e r c h e l e t é l é p h o n e a v e c ce l i b e l l e
foreach ( Telephone t e l in Te le p h o n e s )
{
i f ( t e l . G e t L i b e l l e ( ) == l i b e l l e )
{
// On c r é e un t r o u dans l e t a b l e a u
T e le ph o ne s [ Array . IndexOf ( Telephones , t e l ) ] = null ;
return true ;
10
Chapitre 1 : ABC de la conception et programmation objet
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
}
}
return f a l s e ;
}
public override string To S tr i n g ( )
{
S t r i n g B u i l d e r r e t o u r = new S t r i n g B u i l d e r ( ) ;
r e t o u r . AppendFormat ( ” {0} , {1} ,\ n{2} {3}\ nTéléphone ( s )
NumeroRue , Rue , CodePostal , V i l l e ) ;
foreach ( Telephone t e l in Te le p h o n e s )
{
i f ( t e l != null )
r e t o u r . Append ( t e l ) ;
}
return r e t o u r . To S t ri n g ( ) ;
}
}
}
public c l a s s Telephone
{
private string m _ l i b e l l e ;
public string L i b e l l e {
get {
return m _ l i b e l l e ;
}
private s e t {
Regex myRegex = new Regex ( ” ^ [ a−zA−Z ] { 4 , 1 6 } $ ” ) ;
i f ( myRegex . IsMatch ( l i b e l l e ) )
{
m_libelle = l i b e l l e ;
}
else
{
throw new ArgumentException ( ”Le l i b e l l é e s t i n v a l i d e ” ) ;
}
}
private string m_numero ;
public string Numero {
get {
return m _ l i b e l l e ;
}
private s e t {
Regex myRegex = new Regex ( ” ^[0 −9]{10} $ ” ) ;
i f ( myRegex . IsMatch ( numero ) )
{
m_numero = numero ;
}
else
{
throw new ArgumentException ( ”Le numéro e s t i n v a l i d e ” ) ;
11
:\n” ,
Rémy Malgouyres, www.malgouyres.org
36
37
38
39
Conception Objet et Programmation en C#
}
}
}
}
1.3.3 Classe de tests
Voici la calsse de tests correspondant à la classe Adresse :
UrBooksLTD/UrBooksLTD/TestMetierAdresse.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System ;
using System . Text ;
using M e t i e r ;
namespace UrBooksLTD
{
class TestMetierAdresse : I T e s t I n t e r f a c e
{
public void Test ( )
{
try
{
A d r e s s e a d r e s s e = new A d r e s s e ( ” 12 b i s ” , ” P l a c e de l a Bourse ” , ”
63000 ” , ” Clermont−Ferrand ” ) ;
a d r e s s e . AddTelephone ( ” f i x e ” , ” 0123456789 ” ) ;
a d r e s s e . AddTelephone ( ” asupprimer ” , ” 9876543210 ” ) ;
a d r e s s e . AddTelephone ( ” m o b i l e ” , ” 0623456789 ” ) ;
a d r e s s e . RemoveTelephone ( ” asuppr imer ” ) ;
C on s o l e . WriteLine ( a d r e s s e ) ;
}
catch ( E x c e p t i o n e )
{
C on s o l e . WriteLine ( ” Erreur : ” + e . Message + ” \n” + e . StackTrace )
;
}
}
}
}
1.4 Pattern Strategy
Comme nous l’avons vu dans la partie 1.1.4, nous devons représenter des clients, des fournisseurs
et des éditeurs, qui peuvent être, selon les cas, des personnes morales (entreprises) ou des
personnes physiques (particuliers). Les informations sur une personne morale (raison sociale,
personne à contacter, etc.) ne sont pas forcément les mêmes que les informations sur une
personne physique (nom, prénom, civilité, etc.). On souhaite cependant factoriser les données
et opérations communues aux deux types de personnes (comme les données et opérations liées
à l’adresse de la personne/entreprise).
12
Chapitre 1 : ABC de la conception et programmation objet
1.4.1 Modélisation statique
On se propose de créer trois classes, une classe PersonnePhysiqueOuMorale, générique, qui
factorisera les traits communs à tous les types de personnes, une classe PersonnePhysique, et
une classe PersonneMorale.
Ces classes sont liées par des relations d’héritage, aussi appelées des relations de généralisation (ou de spécialisation, selon le point de vue). Ainsi, une PersonnePhysique est un cas
particulier de PersonnePhysiqueOuMorale. Une PersonneMorale est aussi un cas particulier
de PersonnePhysiqueOuMorale.
Diag 3. e pattern Strategy pour les types de personnes.
1.4.2 Code source
Voici le code de la classe PersonnePhysiqueOuMorale, suivi du code de la classe PersonneMorale
et enfin de la classe PersonnePhysique.
Notez le chaînage des constructeur par lequel le constructeur d’une classe dérivée (par
exemple le constructeur de PersonnePhysique fait appel (avant même le corps du constructeur)
au constructeur de la classe de base PersonnePhysiqueOuMorale pour initialiser les propriétés
de la claase de base.
UrBooksLTD/Metier/PersonnePhysiqueOuMorale.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
using System ;
using System . Text ;
namespace M e t i e r
{
public c l a s s PersonnePhysiqueOuMorale
{
protected string D e s c r i p t i o n { g e t ; s e t ; } // A j o u t e r d e s t e s t s par
Regex s i b e s o i n
protected A d r e s s e m_adresse ;
public PersonnePhysiqueOuMorale ( string d e s c r i p t i o n , A d r e s s e a d r e s s e )
13
Rémy Malgouyres, www.malgouyres.org
14
15
16
17
18
19
20
21
22
23
24
25
Conception Objet et Programmation en C#
{
Description = description ;
m_adresse = a d r e s s e ;
}
public override string To S tr i n g ( )
{
return ” Personne p h y s i q u e ou p e rs o n n e morale
+ D e s c r i p t i o n + ” ,\ n” + m_adresse ;
}
:\n”
}
}
UrBooksLTD/Metier/PersonneMorale.cs
1
2
3
4
5
6
7
8
9
10
using System ;
using System . Text ;
namespace M e t i e r
{
public c l a s s PersonneMorale : PersonnePhysiqueOuMorale
{
public string R a i s o n S o c i a l e { g e t ; s e t ; } // A j o u t e r d e s t e s t s par Regex
si besoin
public string ContactName { g e t ; s e t ; } // A j o u t e r d e s t e s t s par Regex
si besoin
11
12
public PersonneMorale ( string r a i s o n S o c i a l e , string contactName , A d r e s s e
adresse )
// Appel du c o n s t r u c t e u r de l a c l a s s e de b a s e
PersonnePhysiqueOuMorale
: base ( r a i s o n S o c i a l e + ” , c o n t a c t e r ” + contactName , a d r e s s e )
{
RaisonSociale = raisonSociale ;
ContactName = contactName ;
}
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public override string To S tr i n g ( )
{
return ” Personne Morale :\n”
+ R a i s o n S o c i a l e + ” \nà l ’ a t t e n t i o n de ” + ContactName + ” ,\ n”
+ m_adresse ;
}
}
}
UrBooksLTD/Metier/PersonnePhysique.cs
1
2
3
4
5
6
7
using System ;
using System . Text ;
namespace M e t i e r
{
public c l a s s PersonnePhysique
: PersonnePhysiqueOuMorale
14
Chapitre 1 : ABC de la conception et programmation objet
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
public enum C i v i l i t e {MR, MME, MLLE}
private s t a t i c string G e t C i v i l i t e S t r i n g ( C i v i l i t e c i v i l i t e )
{
switch ( c i v i l i t e ) {
case C i v i l i t e .MLLE : return ” M a d e m o i s e l l e ” ;
case C i v i l i t e .MME : return ”Madame” ;
case C i v i l i t e .MR : return ” Monsieur ” ;
default
}
public string Nom { g e t ; s e t ; } // A j o u t e r d e s t e s t s par Regex s i b e s o i n
public string Prenom { g e t ; s e t ; } // A j o u t e r d e s t e s t s par Regex s i
besoin
public C i v i l i t e m _ c i v i l i t e ;
24
25
26
public PersonnePhysique ( string nom , string prenom , C i v i l i t e c i v i l i t e ,
Adresse a d r e s s e )
// Appel du c o n s t r u c t e u r de l a c l a s s e de b a s e
PersonnePhysiqueOuMorale
: base ( G e t C i v i l i t e S t r i n g ( c i v i l i t e ) + ” ” + prenom + ” ” + nom ,
adresse )
{
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
: return ” ” ;
}
Nom = nom ;
Prenom = prenom ;
m_civilite = c i v i l i t e ;
}
public string G e t C i v i l i t e ( )
{
return G e t C i v i l i t e S t r i n g ( m _ c i v i l i t e ) ;
}
public override string To S tr i n g ( )
{
return ” Personne P h y s i q u e \n” + base . D e s c r i p t i o n ;
}
}
}
1.4.3 Classe de Test
Voici la calsse de tests correspondant à l’ensempble du pattern Strategy pour représenter les
personnes :
UrBooksLTD/UrBooksLTD/TestMetierPersonne.cs
1
2
3
4
5
using System ;
using System . Text ;
using M e t i e r ;
15
Rémy Malgouyres, www.malgouyres.org
6
7
8
9
10
11
12
13
14
15
namespace UrBooksLTD
{
class TestMetierPersonne : I T e s t I n t e r f a c e
{
public void Test ( )
{
try
{
A d r e s s e a d r e s s e = new A d r e s s e ( ” 12 b i s ” , ” P l a c e de l a Bourse ” , ”
63000 ” , ” Clermont−Ferrand ” ) ;
16
17
18
// Test de per son n e morale
PersonnePhysiqueOuMorale p e r s o n n e = new PersonneMorale ( ”
L i b r a i r i e Les Bouts qun ” , ” C h r i s t i n e Kafka ” ,
adresse ) ;
C on s o l e . WriteLine ( p e r s o n n e ) ;
PersonneMorale p e r s o n n e M o r a l e = ( p e r s o n n e as PersonneMorale ) ;
i f ( p e r s o n n e M o r a l e != null )
{
C on s o le . WriteLine ( ” Notre c o n t a c t c h e z \” ” + p e r s o n n e M o r a l e .
RaisonSociale
+ ” \” e s t : ” + p e r s o n n e M o r a l e .
ContactName + ” \n” ) ;
}
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Test de per son n e p h y s i q u e
p e r s o n n e = new PersonnePhysique ( ” Kafka ” , ” C h r i s t i n e ” ,
PersonnePhysique . C i v i l i t e .MME, a d r e s s e ) ;
C on s o l e . WriteLine ( p e r s o n n e ) ;
PersonnePhysique p e r s o n n e P h y s i q u e = ( p e r s o n n e as
PersonnePhysique ) ;
i f ( p e r s o n n e P h y s i q u e != null )
{
Co n s o l e . WriteLine ( ” \nOn s ’ a d r e s s e à ” + p e r s o n n e P h y s i q u e .
Prenom
+ ” en d i s a n t : ” + p e r s o n n e P h y s i q u e .
GetCivilite () ) ;
}
34
35
36
37
38
39
40
41
42
43
44
45
46
47
Conception Objet et Programmation en C#
}
catch ( E x c e p t i o n e )
{
C on s o l e . WriteLine ( ” Erreur
;
}
}
}
}
16
: ” + e . Message + ” \n” + e . StackTrace )
Chapitre 1 : ABC de la conception et programmation objet
1.5 Diagrammes de Séquence
1.5.1 Notion de diagramme de séquence et exemple simple
Les diagrammes de séquences permettent d’avoir une vue dynamique des intéractions entre
classes et/ou des algorithmes. Généralement, lorsqu’un acteur lance une action (comme un
click de bouton) dans l’application, un certain nombre de classes interviennent dans la mise en
oeuvre de l’action par le programme. Les méthodes de ces classes s’appellent les unes les autres
au cours du temps. De plus, à l’intérieur de ces méthodes, des boucles (des loop correspondant à
des while ou for), des branchements conditionnels (opt pour un if ou alt pour un if...else ou
un switch) implémentent des algotithmes. En bref, les diagrammes de séquences schématisent
l’algorithmique inter classes et parfois l’algorithmique intra classe.
Nous voyons ici un exemple simple. Une classe MaximumTableau, possède un constructeur
qui crée un tableau aléatoire d’entiers, dont la référence est mémorisée dans un attribut. Une
méthode CalculeMaximum retourne la plus grande des valeurs contenues dans le tableau.
Le programme principal crée une instance de MaximumTableau, appelle la méthode de
MaximumTableau qui calcule le maximum, et affiche le résultat.
Voici tout d’abord le diagramme de conception statique (diagramme de classes) :
Diag 4. Diagramme de Classes pour le programme
créant un tableau aléatoire et calculant son maximum.
Voici maintenant le diagramme de séquence de l’algorithme grossier (inter classes) pour le
même programme, qui schématise les appels de méthodes entre la classe Program et la classe
MaximumTableau.
Dans ce diagramme, l’axe vertical représente le temps. Les lignes verticales représentent
des cycles de vies de classes ou d’instances de classes.
La flèche avec le stéréotype create est un appel du constructeur de MaximumTableau, lequel
génère le tableau aléatoire. La flèche continue est un appel de la méthode CalculeMaximum
dans la fonction Program.Main.
Les rectangles verticaux représentent des méthodes (appelés messages) et les flèches en
pointillés qui en sont issues des valeurs retournées par ces méthodes (appelés messages de
retour).
17
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
Diag 5. Diagramme de Séquences “vu de loin” (algorithmique
inter classe) pour le programme créant un tableau aléatoire et
calculant son maximum.
Voici enfin l’algorithme détaillé pour le même programme, qui inclut l’algorithme de calcul
du maximum à l’intérieur de la méthode MaximumTableau.CalculeMaximum
Diag 6. Diagramme de Séquences “vu de près” (incluant
l’algorithmique intra classe) pour le programme créant un
tableau aléatoire et calculant son maximum.
Voici maintenant le code source C#de la classe MaximumTableau, puis de la classe Program.
ExemplesDiagSeq/ExemplesDiagSeq/Program.cs
1
2
3
4
5
using System ;
using System . Text ;
namespace ExemplesDiagSeq
18
Chapitre 1 : ABC de la conception et programmation objet
6
7
8
9
10
11
12
13
14
15
16
17
{
c l a s s Program
{
s t a t i c void Main ( string [ ] a r g s )
{
MaximumTableau myTableau = new MaximumTableau ( 5 0 ) ;
int max = myTableau . CalculeMaximum ( ) ;
C o n s o le . WriteLine ( ”Maximum du Tableau : {0} ” , max) ;
C o n s o le . ReadKey ( ) ;
}
}
}
ExemplesDiagSeq/ExemplesDiagSeq/MaximumTableau.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System ;
using System . Text ;
namespace ExemplesDiagSeq
{
c l a s s MaximumTableau
{
// Générateur a l é a t o i r e , OBLIGATOIREMENT s t a t i q u e ,
// I n i t i a l i s é au d é b u t du programme par l e c o n s t r u c t e u r
private s t a t i c Random random = new Random ( ) ;
private int [ ] m_tableau ;
public MaximumTableau ( int nbElements )
{
m_tableau = new int [ nbElements ] ;
fo r ( int i = 0 ; i < nbElements ; i ++)
{
m_tableau [ i ] = random . Next ( 0 , 1 0 0 ) ;
}
}
public int CalculeMaximum ( )
{
int max = −1 ;
foreach ( int x in m_tableau )
{
i f ( x > max)
{
max = x ;
}
}
return max ;
}
}
}
19
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
1.5.2 Exemple de diagramme de séquence pour UrBookLTD
Un diagramme de séquence correspond à une action (avec éventuellement des hypothèses sur
les branchements). Recenser les acteurs et les actions fait partie de l’analyse fonctionnelle de
l’application.
Nous avons recencé dans la partie 1.1.3 les principales actions pour l’acteur Client et pour
l’acteur Manager. Voyons un exemple de diagramme de séquence pour l’action modifier ses
données personnelles d’un Client.
Deux cas sont possibles : le client est une personne physique ou une personne morale. On
fait saisir les données au client, y compris concernant son adresse et son numéro de téléphone.
On affiche enfin le résultat.
20
Chapitre 1 : ABC de la conception et programmation objet
Diag 7. Diagramme de Séquences de la saisie des données personnelles par une personne
(physique ou morale).
21
Chapitre 2
Delegates et Expressions Lambda
2.1 Fonctions de comparaison basiques
Considérons trois types qui admettent la comparaison (un ordre) entre les éléments :
• Le type int avec la relations d’ordre usuelle sur les nombres entiers ;
• Le type string avec l’ordre alphabétique ;
• Le type MyDate défini ci-dessous avec l’ordre chronologique sur les dates.
Voici la définition de la classe MyDate :
CoursLinq/ExemplesDelgate/ex02_MyDate.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
System . Text . R e g u l a r E x p r e s s i o n s ;
namespace ExemplesDelegate
{
public p a r t i a l c l a s s MyDate
{
public int Annee { g e t ; private s e t ; }
public int Mois { g e t ; private s e t ; }
public int Jour { g e t ; private s e t ; }
public MyDate ( string dateJJMMAAA)
{
string [ ] e l e m e n t s D a t e = dateJJMMAAA . S p l i t (new [ ] { ’ / ’ } ) ;
Annee = int . Parse ( e l e m e n t s D a t e [ 2 ] ) ;
Mois = int . Parse ( e l e m e n t s D a t e [ 1 ] ) ;
Jour = int . Parse ( e l e m e n t s D a t e [ 0 ] ) ;
}
public override string To S tr i n g ( )
{
return Jour . T o S tr i n g ( ) . PadLeft ( 2 , ’ 0 ’ )
22
Chapitre 2 : Delegates et Expressions Lambda
28
29
30
31
32
33
34
35
36
37
38
39
+ ”/” + Mois . T o S t ri n g ( ) . PadLeft ( 2 , ’ 0 ’ ) + ”/ ”
+ Annee . T oS t r i ng ( ) . PadLeft ( 2 , ’ 0 ’ ) ;
}
public string T o S t r i n g R e v e r s e ( )
{
return Annee . T o S t ri n g ( ) . PadLeft ( 2 , ’ 0 ’ )
+ ”/” + Mois . T o S t ri n g ( ) . PadLeft ( 2 ,
+ Jour . T oS t r i ng ( ) . PadLeft ( 2 , ’ 0 ’ ) ;
}
’ 0 ’ ) + ”/ ”
}
}
Nous pouvons définir trois fonctions différentes pour coparer respectivement ces trois types
de données. Le fonctionnement est à peu près le même, chaque méthode renvoie une valeur
négative, nulle ou positive suivant les cas sur l’ordre ou l’égalité des deux paramètres (comme
la fonction strcmp pour le langage C).
CoursLinq/ExemplesDelgate/ex01_ExCompareBasic.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s ExCompareBasic
{
// Fo nc t i on de comparaison de deux e n t i e r s
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int ? CompareInt ( int ? a , int ? b )
{
return a − b ;
}
// Fo nc t i on de comparaison de deux c h a î n e par o r d r e a l p h a b é t i q u e
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int CompareString ( string a , string b )
{
return S t r i n g . Compare ( a , b ) ;
}
// Fo nc t i on de comparaison de deux d a t e s par o r d r e a l p h a b é t i q u e
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int CompareDate ( MyDate a , MyDate b )
{
return S t r i n g . Compare ( a . T o S t r i n g R e v e r s e ( ) , b . T o S t r i n g R e v e r s e ( ) ) ;
}
}
}
Ces méthodes s’utilisent comme dans les méthodes de test suivantes :
CoursLinq/ExemplesDelgate/ex03_TestCompareBasic.cs
23
Rémy Malgouyres, www.malgouyres.org
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using
using
using
using
using
Conception Objet et Programmation en C#
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s TestCompareBasic
{
// Test de l a comparaison de deux e n t i e r s
public s t a t i c void TestCompareInt ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r deux e n t i e r s s é p a r é s pas un / : ” ) ;
string [ ] chainesAB = C o ns o l e . ReadLine ( ) . S p l i t (new [ ] { ’ / ’ } ) ;
int a = int . Parse ( chainesAB [ 0 ] ) ;
int b = int . Parse ( chainesAB [ 1 ] ) ;
i f ( ExCompareBasic . CompareInt ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( ExCompareBasic . CompareInt ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// Test de l a comparaison a l p h a b é t i q u e de deux c h a î n e s
public s t a t i c void TestCompareString ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r deux c h a î n e s a l a p h a b é t i q u e s s é p a r é e s
pas un / : ” ) ;
string [ ] chainesAB = C o ns o l e . ReadLine ( ) . S p l i t (new [ ] { ’ / ’ } ) ;
string a = chainesAB [ 0 ] ;
string b = chainesAB [ 1 ] ;
i f ( ExCompareBasic . CompareString ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( ExCompareBasic . CompareString ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// Test de l a comparaison c h r o n o l o g i q u e de deux d a t e s
public s t a t i c void TestCompareDate ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r une d a t e au f o r ma t j j /mm/ aaaa : ” ) ;
MyDate a = new MyDate ( C o n s o l e . ReadLine ( ) ) ;
C o n s o le . Write ( ” Merci d ’ e n t r e r une a u t r e d a t e au f o r ma t j j /mm/ aaaa
”) ;
MyDate b = new MyDate ( C o n s o l e . ReadLine ( ) ) ;
i f ( ExCompareBasic . CompareDate ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
24
:
Chapitre 2 : Delegates et Expressions Lambda
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
i f ( ExCompareBasic . CompareDate ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// Méthode q u i e x é c u t e l e s t e s t s d e s t r o i s méthodes de comparaison
public s t a t i c void TestAllCompareMethods ( )
{
TestCompareInt ( ) ;
TestCompareString ( ) ;
TestCompareDate ( ) ;
}
}
}
2.2 Uniformisation de la signature des fonctions
Pour pouvoir utiliser la comparaison sur des types génériques (par exemple, comme nous le
verrons ci-dessous, pour écrire des algorithmes de tris qui fonctionneront indépendament du
type), nous allons créer des méthodes de comparaison dont la signature est générique. Autrement dit, la signature des trois méthodes de comparaison est la même, ainsi que la
signature des trois méthodes de lecture dans la console.
On s’appuie pour celà sur la classe Objet, qui est ancètre de toutes les classes en C#. Dans
la définition des méthodes, on réalise un downcasting pour utiliser la comparaison sur des types
plus spécialisés que la classe Objet (ici les types int, string et MyDate).
CoursLinq/ExemplesDelgate/ex04_ExUniformSignature.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s ExUniformSignature
{
public s t a t i c Object ReadInt ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r un nombre e n t i e r
int a = int . Parse ( C on s o l e . ReadLine ( ) ) ;
return a ;
}
: ”) ;
public s t a t i c Object Read String ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r une c h a î n e a l p h a b é t i q u e
string a = C o n so l e . ReadLine ( ) ;
return a ;
}
25
: ”) ;
Rémy Malgouyres, www.malgouyres.org
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Conception Objet et Programmation en C#
public s t a t i c Object ReadDate ( )
{
C o n s o le . Write ( ” Merci d ’ e n t r e r une d a t e au f o r ma t j j /mm/ aaaa
MyDate a = new MyDate ( C o n s o l e . ReadLine ( ) ) ;
return a ;
}
: ”) ;
// Fo nc t i on de comparaison de deux e n t i e r s
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int CompareInt ( Object a , Object b )
{
int ? aa = a as int ? ;
int ? bb = b as int ? ;
return aa == bb ? 0 : ( aa < bb ? −1 : 1 ) ;
}
// Fo nc t i on de comparaison de deux c h a î n e par o r d r e a l p h a b é t i q u e
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int CompareString ( Object a , Object b )
{
string aa = a as string ;
string bb = b as string ;
return S t r i n g . Compare ( aa , bb ) ;
}
// Fo nc t i on de comparaison de deux d a t e s par o r d r e c h r o n o l o g i q u e
// Retourne 0 s i l e s a==b , p o s i t i f s i a<b , n é g a t i f s i b<a
public s t a t i c int CompareDate ( Object a , Object b )
{
MyDate aa = a as MyDate ;
MyDate bb = b as MyDate ;
return S t r i n g . Compare ( aa . T o S t r i n g R e v e r s e ( ) , bb . T o S t r i n g R e v e r s e ( ) ) ;
}
}
}
Dans les trois fonctions de test suivantes, on constate que le code est exactement le même,
sauf le nom des méthodes et les downcasts. Comme nous allons le voir dans la partie suivante,
on peut en fait factoriser ce code en passant les fonctions de lecture dans la console et de
comparaison en paramètre.
CoursLinq/ExemplesDelgate/ex05_TestUniformSignature.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s T e s t U n i f o r m S i g n a t u r e
{
public s t a t i c void TestCompareInt ( )
{
int ? a = ExUniformSignature . ReadInt ( ) as int ? ;
26
Chapitre 2 : Delegates et Expressions Lambda
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
int ? b = ExUniformSignature . ReadInt ( ) as int ? ;
i f ( ExUniformSignature . CompareInt ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( ExUniformSignature . CompareInt ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
public s t a t i c void TestCompareString ( )
{
string a = ExUniformSignature . Read Str ing ( ) as string ;
string b = ExUniformSignature . R ead Str ing ( ) as string ;
i f ( ExUniformSignature . CompareString ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( ExUniformSignature . CompareString ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
public s t a t i c void TestCompareDate ( )
{
Object a = ExUniformSignature . ReadDate ( ) ;
Object b = ExUniformSignature . ReadDate ( ) ;
i f ( ExUniformSignature . CompareDate ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( ExUniformSignature . CompareDate ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// Méthode q u i e x é c u t e l e s t e s t s d e s t r o i s méthodes de comparaison
public s t a t i c void TestAllCompareMethods ( )
{
TestCompareInt ( ) ;
TestCompareString ( ) ;
TestCompareDate ( ) ;
}
}
}
2.3 Types delegate
Définition. Un type délégué est un type de donnée pouvant contenir des méthodes C#. Pour
un type délégué donné, la signature des méthodes est fixée, dans la définition du type délégué.
27
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
Les types délégués sont analogues aux types pointeurs de fonctions en langage C : ils peuvent
contenir des fonctions.
Exemple. Voici la déclaration d’un type délégué qui peut être utilisé pour des méthodes de
comparaison.
1
2
3
4
5
6
7
// Type d é l é g u é pour l e s f o n c t i o n s de comparaison
// s i g n a t u r e : i n t f o n c t i o n ( O b j e c t , O b j e c t )
public delegate int MonTypeFonctionComparaison ( Object a , Object b ) ;
// Ajout d ’ une é t h o d e dans une i n s t a n c e de d é l é g u é ( abonnement ) .
// La méthode d o i t a v o i r l a même s i g n a t u r e que l e t y p e d é l é g u é
MonTypeFonctionLecture r e a d F u n c t i o n = ExUniformSignature . ReadInt ;
En fait, nous verrons plus loin que les instances de types délégués en C#peuvent contenir
plusieurs méthodes de même signature. On parle alors de délégués multicast.
Voici maintenant des exemples d’utilisation des délégués pour créer des méthodes qui peuvent
s’appliquer à la comparaison d’objets de différents types.
La première méthode, plus simple, montre comment on affecte une méthode (ici ExUniformSignature.R
ou ExUniformSignature.CompareInt). Dans la deuxième méthodes, les instances de types déléguées sont en paramètre de la fonction, ce qui permet, lors de l’appel de la fonction, de passer
la méthode correspondant au type (int, string et MyDate) utilisé (voir troisième méthode
ci-dessous).
CoursLinq/ExemplesDelgate/ex06_BasicDelegateExemples.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s B a s i c D e l e g a t e E x e m p l e s
{
// //////////////////////////////////////////////////////////////
// D é c l a r a t i o n de t y p e s d é l é g u é s
// Type d é l é g u é pour l e s f o n c t i o n s de l e c t u r e
// s i g n a t u r e : O b j e c t f o n c t i o n ( v o i d )
public delegate Object MonTypeFonctionLecture ( ) ;
// Type d é l é g u é pour l e s f o n c t i o n s de comparaison
// s i g n a t u r e : i n t f o n c t i o n ( O b j e c t , O b j e c t )
public delegate int MonTypeFonctionComparaison ( Object a , Object b ) ;
// /////////////////////////////////////////////////////////////
// Exemples d ’ u t i l i s a t i o n de d é l é g u é s
// Exemple de méthode u t i l i s a n t d e s v a r i a b l e s l o c a l e s de t y p e d é l é g u é
public s t a t i c void ExempleDelegatesUnicastDeBase ( )
{
// Ajout d ’ une é t h o d e dans une i n s t a n c e de d é l é g u é ( abonnement ) .
28
Chapitre 2 : Delegates et Expressions Lambda
30
31
32
33
34
35
// La méthode d o i t a v o i r l a même s i g n a t u r e que l e t y p e d é l é g u é
MonTypeFonctionLecture r e a d F u n c t i o n = ExUniformSignature . ReadInt ;
// Ajout d ’ une é t h o d e dans une i n s t a n c e de d é l é g u é ( abonnement ) .
// La méthode d o i t a v o i r l a même s i g n a t u r e que l e t y p e d é l é g u é
MonTypeFonctionComparaison compareFunction = ExUniformSignature .
CompareInt ;
36
37
// E x é c u t i o n de l a méthode c o n t e n u e dans l ’ i n s t a n c e de d é l é g u é
readFunction
// Syntaxiquement , ça f o n c t i o n n e comme un a p p e l de f o n c t i o n
// Le t y p e
Object a = r e a d F u n c t i o n ( ) ;
Object b = r e a d F u n c t i o n ( ) ;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// E x é c u t i o n de l a méthode c o n t e n u e dans l ’ i n s t a n c e de d é l é g u é
compareFunction
i f ( compareFunction ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( compareFunction ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// Passage de p a r a m è t r e s de t y p e d é l é g u é
// La méthode i c i d é f i n i e marche pour nos t r o i s t y p e s i n t , s t r i n g e t
MyDate
// Les méthodes de l e c t u r e e t de comparaison s o n t p a s s é e s en paramètre
!!!!
public s t a t i c void TestCompareGeneric ( MonTypeFonctionLecture
readFunction ,
MonTypeFonctionComparaison
compareFunction )
{
Object a = r e a d F u n c t i o n ( ) ;
Object b = r e a d F u n c t i o n ( ) ;
i f ( compareFunction ( a , b ) == 0 )
C on s o l e . WriteLine ( ” {0} é g a l e {1} ” , a , b ) ;
else
i f ( compareFunction ( a , b ) < 0 )
C on s o le . WriteLine ( ” {0} i n f é r i e u r à {1} ” , a , b ) ;
else
C on s o le . WriteLine ( ” {0} s u p é r i e u r à {1} ” , a , b ) ;
}
// U t i l i s a t i o n d ’ une méthode a v e c p a r a m è t r e s de t y p e d é l é g u é
public s t a t i c void t e s t P a s s a g e P a r a m e t r e D e l e g u e ( )
{
TestCompareGeneric ( ExUniformSignature . ReadInt , ExUniformSignature .
CompareInt ) ;
TestCompareGeneric ( ExUniformSignature . ReadString , ExUniformSignature .
CompareString ) ;
29
Rémy Malgouyres, www.malgouyres.org
77
78
79
80
Conception Objet et Programmation en C#
TestCompareGeneric ( ExUniformSignature . ReadDate , ExUniformSignature .
CompareDate ) ;
}
}
}
2.4 Exemple d’utilisation : méthode de tri générique
Voici une méthode de tri, implémentation de l’algorithme du tri par sélection, qui peut fonctionner pour nos trois types de données : int, string et MyDate. La liste des objets à trier,
ainsi que la méthode de comparaison à utiliser pour comparer les éléments de la liste, sont
passées en paramètre.
CoursLinq/ExemplesDelgate/ex07_ExTriGenerique.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s ExTriGenerique
{
// Type d é l é g u é pour l e s f o n c t i o n s de comparaison
// s i g n a t u r e : i n t f o n c t i o n ( O b j e c t , O b j e c t )
public delegate int MonTypeFonctionComparaison ( Object a , Object b ) ;
// Méthode de t r i q u i f o n c t i o n n e pour t o u s nos t y p e s de données : i n t ;
s t r i n g ou MyDate
// La f o n c t i o n p e r m e t t a n t de comparer l e s é l é m e n t s à t r i e r e s t p a s s é e en
paramètre
// La l i s t e e s t p a s s é e par r é f é r e n c e e t l a r é f é r e n c e e s t m o d i f i é e à l a
f i n de l a f o n c t i o n
//
// L ’ a l g o r i t h m e u t i l i s é pour ce t r i ”à l a main” e s t l e t r i par s é l e c t i o n
:
// t a n t que l a l i s t e e s t non v i d e
//
c h e r c h e r l e p l u s p e t i t é l é m e n t min de l a l i s t e
//
a j o u t e r min en queue de l i s t e au r é s u l t a t
//
sup p r im e r min de l a l i s t e
public s t a t i c void T r i S e l e c t i o n ( r e f L i s t <Object> l i s t e ,
MonTypeFonctionComparaison fonctionCompare )
{
L i s t <Object> l i s t e T r i e e = new L i s t <Object >() ;
// t a n t que l a l i s t e e s t non v i d e
while ( l i s t e . Count ( ) != 0 )
{
// Recherche du minimum de l a l i s t e r e s t a n t e :
Object min = l i s t e . F i r s t ( ) ;
foreach ( Object e l e m e n t in l i s t e )
{
i f ( fonctionCompare ( element , min ) < 0 )
30
Chapitre 2 : Delegates et Expressions Lambda
36
37
38
39
40
{
min = e l e m e n t ;
}
}
l i s t e T r i e e . Add( min ) ;
résultat
l i s t e . Remove ( min ) ;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// a j o u t e r min en queue de l i s t e au
// s up p r i m e r min de l a l i s t e
}
// Retour de l a l i s t e t r i é e par r é f é r e n c e
liste = listeTriee ;
}
// Gé nér a tion d ’ une l i s t e ( non ordonnée ) de MyDate
public s t a t i c L i s t <Object> g e n e r a t e I n i t i a l L i s t ( )
{
L i s t <Object> l i s t e D a t e s = new L i s t <Object >() ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2012 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2015 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/10/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 10/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2013 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/08/2015 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 12/09/2013 ” ) ) ;
return l i s t e D a t e s ;
}
// Méthode de t e s t de l a méthode de t r i g é n é r i q u e , a p p l i q u é e à une l i s t e
de d a t e s .
public s t a t i c void T e s t T r i B u l l e D e l e g a t e ( )
{
// Gé nér a tion de l a l i s t e de MyDate
L i s t <Object> l i s t e D a t e s = g e n e r a t e I n i t i a l L i s t ( ) ;
// Appel de l a méthode de t r i a v e c paramètre d é l é g u é pour
// l a méthode de comaraison
T r i S e l e c t i o n ( r e f l i s t e D a t e s , ExUniformSignature . CompareDate ) ;
// A f f i c h a g e de l a l i s t e t r i é e
foreach ( MyDate d a t e in l i s t e D a t e s )
C on s o l e . Write ( d a t e + ” , ” ) ;
C o n s o le . WriteLine ( ) ;
}
}
}
2.5 Expressions lambda
Les Expressions lambda sont un moyen de définir des méthodes anonymes en C#. Voici un
exemple de méthode de comparaison défini par une expression lambda, que nous passerons en
paramètre à la méthode ExTriGenerique.TriSelection.
CoursLinq/ExemplesDelgate/ex08_ExempleExpressionLambda.cs
31
Rémy Malgouyres, www.malgouyres.org
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s ExempleExpressionLambda
{
public s t a t i c void TestTriBulleExprssionLambda ( )
{
L i s t <Object> l i s t e D a t e s = ExTriGenerique . g e n e r a t e I n i t i a l L i s t ( ) ;
// U t i l i s a t i o n d ’ une e x p r e s s i o n lambda ( méthode anonyme )
// Paramètres de l a méthode anonyme : a e t b de t y p e O b j e c t
// Retour de l a méthode anonyme : i n t ( t y p e d é t e r m i n é dynamiquement
par l e r e t u r n )
ExTriGenerique . T r i S e l e c t i o n ( r e f l i s t e D a t e s , ( Object a , Object b ) =>
{
MyDate aa = a as MyDate ;
MyDate bb = b as MyDate ;
return S t r i n g . Compare ( aa . T o S t r i n g R e v e r s e ( ) , bb . T o S t r i n g R e v e r s e ( )
);
}) ;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Conception Objet et Programmation en C#
// A f f i c h a g e de l a l i s t e t r i é e
foreach ( MyDate d a t e in l i s t e D a t e s )
C on s o l e . Write ( d a t e + ” , ” ) ;
C o n s o le . WriteLine ( ) ;
}
}
}
32
Chapitre 3
Collections
Les Collections sont des classes qui gèrent des structures de données représentant des ensembles,
listes, tableaux, etc. d’objets. Les opérations de base des collections sont typiquement l’ajout, la
suppression d’éléments, le tri suivant un certain ordre (utilisant une fonction de comparaison),
etc.
Certaines collections, comme les files (gestion First In First Out ou encore FIFO) et les piles
(gestion Last In First Out LIFO), ont un ensemble de primitives spécifiques, qui permettent
une gestion particulières des éléments qui ont de bonnes propriétés algorithmiques.
Les dictionnaires permettent, eux, de représenter des applications (au sens mathématique,
associant à un élément d’un ensemble un élément d’un autre ensemble). Un dictionnaire associe
à différentes clefs des valeurs. Chaque clef est unique (pas de doublon) et sa valeur est données
par le dictionnaire.
Nous abordons ici les collections appelées génériques, parce qu’elles dépendent d’un template, à savoir, le type de données qui constitue les éléments de la collection (ou encore les clefs
et les valeurs dans le cas d’un dictionnaire). Elles se trouvent dans le namespace suivant :
System.Collections.Generic
Dont la documentation peut être consultée via la MSDN en ligne ou dans la visionneuse
d’aire de Visual Studio.
Figure 3.1 : Capture de la Visionneuse d’Aide de Visual Studio
33
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
3.1 Listes
CoursLinq/ExemplesCollections/ex01_ExempleList.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace E x e m p l e s C o l l e c t i o n s
{
public c l a s s ExempleList
{
public s t a t i c void Ex em pl e L is t e ( )
{
// D é c l a r a t i o n d ’ une r é f é r e n c e s u r d e s L i s t <i n t >
L i s t <int> l i s t e ;
// A l l o c a t i o n OBLIGATOIRE : c r é a t i o n d ’ une l i s t e v i d e
// ( par exemple dans un c o n s t r u c t e u r , pour i n i t i a l i s e r un a t t r i b u t
liste )
l i s t e = new L i s t <int >() ;
// La méthode Add a j o u t e un é l é m e n t en queue de l i s t e
l i s t e . Add ( 4 ) ; // é l é m e n t s : 4
l i s t e . Add ( 7 ) ; // é l é m e n t s : 4 , 7
l i s t e . Add ( 9 ) ; // é l é m e n t s : 4 , 7 , 9
l i s t e . Add ( 7 ) ; // é l é m e n t s : 4 , 7 , 9 , 7
// I n s e r t i o n de 34 en t ê t e de l i s t e
l i s t e . I n s e r t ( 0 , 3 4 ) ; // é l é m e n t s : 34 , 4 , 7 , 9 , 7
l i s t e . I n s e r t ( 2 , 1 2 ) ; // é l é m e n t s : 34 , 4 , 12 , 7 , 9 , 7
// S u p p r e s s i o n de l a p r e m i è r e o c c u r e n c e d ’ un é l é m e n t :
l i s t e . Remove ( 7 ) ; // é l é m e n t s : 34 , 4 , 12 , 9 , 7
// Nombre d ’ é l é m e n t s de l a l i s t e :
C o n s o le . WriteLine ( ”Nombre d ’ é l é m e n t s : {0} ” , l i s t e . Count ) ;
// Parcours de l a l i s t e par f o r e a c h : a f f i c h e ”34 , 4 , 12 , 9 , 7 ,”
foreach ( int e l e m e n t in l i s t e )
{
C on s o l e . Write ( e l e m e n t + ” , ” ) ;
}
l i s t e . S o r t ( ) ; // Tri de l a l i s t e
C o n s o le . WriteLine ( ” \ n L i s t e T r i é e :” ) ;
foreach ( int e l e m e n t in l i s t e )
{
C on s o l e . Write ( e l e m e n t + ” , ” ) ;
}
}
}
}
34
Chapitre 3 : Collections
3.2 Files
Une file est une structures de données dans laquelle on peut ajouter et supprimer des éléments
suivant la règle du premier arrivé premier sorti, ou encore F IF O (First In First Out).
Le nom de file vient d’une analogie avec une file d’attente à un guichet, dans laquelle le
premier arrivé sera la premier servi. Les usagers arrivent en queue de file, et sortent de la file
à sa tête.
Les files correspondent à la classe C#Queue<T> Les principales primitives de gestion des
files sont les suivantes :
• Constructeur : cette fonction crée une file vide.
• EstVide : renvoie 1 si la file est vide, 0 sinon.
• EstPleine : renvoie 1 si la file est pleine, 0 sinon.
• AccederTete : cette fonction permet l’accès à l’information contenue dans la tête de file.
• Enfiler : cette fonction permet d’ajouter un élément en queue de file. La fonction renvoie
un code d’erreur si besoin en cas de manque de mémoire.
• Defiler : cette fonction supprime la tête de file. L’élément supprimé est retourné par la
fonction Defiler pour pouvoir être utilisé.
• Vider : cette fonction vide la file.
• Detruire : cette fonction permet de détruire la file.
CoursLinq/ExemplesCollections/ex02_ExempleQueue.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace E x e m p l e s C o l l e c t i o n s
{
public c l a s s ExempleQueue
{
public s t a t i c void ExempleFile ( )
{
// d é c l a r a t i o n d ’ une v a r i a b l e r é f é r e n c e v e r s d e s Queues<i n t >
Queue<int> f i l e ;
// A l l o c a t i o n OBLIGATOIRE : c r é a t i o n d ’ une f i l e v i d e
// ( par exemple dans un c o n s t r u c t e u r , pour i n i t i a l i s e r un a t t r i b u t
file )
f i l e = new Queue<int >() ;
// La méthode Enqueue a j o u t e un é l é m e n t en queue de f i l e (FIFO)
f i l e . Enqueue ( 4 ) ; // é l é m e n t s : 4
f i l e . Enqueue ( 7 ) ; // é l é m e n t s : 4 , 7
f i l e . Enqueue ( 9 ) ; // é l é m e n t s : 4 , 7 , 9
// S u p p r e s s i o n de l a t ê t e de f i l e ( l ’ é l é m e n t supprimé e s t r e t o u r n é )
35
Rémy Malgouyres, www.malgouyres.org
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Conception Objet et Programmation en C#
int t e t e = f i l e . Dequeue ( ) ; // é l é m e n t supprimé : 4 ( g e s t i o n FIFO)
C o n s o le . WriteLine ( ”La t ê t e de f i l e {0} a é t é supprimée ” , t e t e ) ;
C o n s o le . WriteLine ( ”La t ê t e de f i l e e s t maintenant {0} ” , f i l e . Peek ( ) )
;
// Parcours de l a l i s t e par f o r e a c h
foreach ( int e l e m e n t in f i l e )
{
C on s o l e . Write ( e l e m e n t + ” , ” ) ;
}
: a f f i c h e ”7 , 9”
}
}
}
3.3 Dictionnaires
Comme nous l’avons dit, un dictionnaire permet d’associer à des clefs d’un certain type des
valeurs d’un autre type. Voici un exemple dans lequel notre dictionnaire associe à une chaîne
de caractère (type string) représentant le nom d’un aliment, un nombre représentant le poid
de cet aliment en grammes.
CoursLinq/ExemplesCollections/ex03_ExempleDictionnaire.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace E x e m p l e s C o l l e c t i o n s
{
public c l a s s E x e m p l e D i c t i o n n a i r e
{
public s t a t i c void ExempleDico ( )
{
// d é c l a r a t i o n d ’ une v a r i a b l e r é f é r e n c e v e r s d e s D i c t i o n a r y <s t r i n g ,
int>
D i c t i o n a r y <string , int> d i c o P o i d s ;
// A l l o c a t i o n OBLIGATOIRE : c r é a t i o n d ’ une d i c t i o n n a i r e v i d e
// ( par exemple dans un c o n s t r u c t e u r , pour i n i t i a l i s e r un a t t r i b u t
dicoPoids )
d i c o P o i d s = new D i c t i o n a r y <string , int >() ; // p o i d s en gramme d ’ un
aliment
try
{
d i c o P o i d s . Add( ” B a r q u e t t e de f r i t e s ” , 1 0 0 ) ;
d i c o P o i d s . Add( ” Yoghurt n a t u r e ” , 1 2 5 ) ;
d i c o P o i d s . Add( ” Yoghurt aux f r u i t ” , 1 4 0 ) ;
d i c o P o i d s . Add( ” L i t r e de l a i t ” , 1 0 0 0 ) ;
}
catch ( E x c e p t i o n e )
36
Chapitre 3 : Collections
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
{
C on s o l e . WriteLine ( ” Erreur
: ” + e . StackTrace ) ;
}
i f ( d i c o P o i d s . Remove ( ” Yoghurt aux f r u i t ” ) )
{
C on s o l e . WriteLine ( ” S u p p r e s s i o n de Yoghurt aux f r u i t . . . ” ) ;
} else
{
C on s o l e . WriteLine ( ” Yoghurt aux f r u i t : c l e f i n t r o u v a b l e ” ) ;
}
// Parcours de l ’ e n s e m b l e d e s c o u p l e s ( c l e f s , v a l e u r s )
// 1) R é c u p é r a t i o n de l a c o l l e c t i o n d e s c l e f s :
D i c t i o n a r y <string , int >. K e y C o l l e c t i o n l e s C l e f s = d i c o P o i d s . Keys ;
// 2) Parcours de l a c o l l e c t i o n d e s c l e f s e t a f f i c h a g e d e s v a l e u r s
:
foreach ( string c l e f in l e s C l e f s )
{
C on s o l e . WriteLine ( ”Le p o i d s de {0} e s t de {1} grammes” ,
clef , dicoPoids [ c l e f ] ) ;
}
// Parcour de l ’ e n s e m b l e d e s c l e f s v a l e u r s
D i c t i o n a r y <string , int >. V a l u e C o l l e c t i o n l e s V a l e u r s = d i c o P o i d s .
Values ;
C o n s o le . WriteLine ( ” Les p o i d s p o s s i b l e s pour l e s a l i m e n t s p r é s e n t s
s o n t :” ) ;
foreach ( int v a l in l e s V a l e u r s )
{
C on s o l e . WriteLine ( ” {0} grammes , ” , v a l ) ;
}
try
{
C on s o l e . WriteLine ( ” V o i c i l e p o i d s de l a b o î t e d ’ o e u f s
d i c o P o i d s [ ” B o î t e de 6 o e u f s ” ] ) ;
: ”,
}
catch ( KeyNotFoundException )
{
C on s o l e . WriteLine ( ”Euh . . . En f a i t , nous n ’ avons pas d ’ o e u f s . . . ” )
;
}
try
{
// Les d o u b l o n s ne s o n t pas permis : chaque c l e f e s t u n i q u e
// e t e s t a s s o c i é e à une s e u l e v a l e u r
d i c o P o i d s . Add( ” Yoghurt n a t u r e ” , 1 5 0 ) ;
}
catch ( E x c e p t i o n e )
{
C on s o l e . WriteLine ( ” Erreur : chaque c l e f e s t u n i q u e ! ! ! ” ) ;
C on s o l e . WriteLine ( e . StackTrace ) ;
}
37
Rémy Malgouyres, www.malgouyres.org
80
81
82
try
{
// Recherche du pr emier é l é m e n t s a t i s f a i s a n t un p r é d i c a t ( p o i d s
>= 150 grammes )
// Le p r é d i c a t e s t une e x p r e s s i o n lambda p r e n a n t une p a i r e c l e f
valeur
// e t r e t o u r n a n t un b o o l é e n .
KeyValuePair<string , int> c l e f V a l e u r P o i d s S u p e r i e u r A 1 5 0 =
d i c o P o i d s . F i r s t ( p a i r e C l e f V a l e u r => p a i r e C l e f V a l e u r . Value >=
150) ;
C on s o l e . WriteLine ( ”Le pr emier p o i d s s u p é r i e u r à 150 grammes dans
le dictionnaire est ”) ;
C on s o l e . WriteLine ( ” l e p o i d s de {0} , s o i t {1} grammes” ,
c l e f V a l e u r P o i d s S u p e r i e u r A 1 5 0 . Key ,
c l e f V a l e u r P o i d s S u p e r i e u r A 1 5 0 . Value ) ;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
Conception Objet et Programmation en C#
}
catch
{
C on s o l e . WriteLine ( ”Aucune é l é m e n t d ’ a son p o i d s s u p é r i e u r à 150
grammes . ” ) ;
}
}
}
}
3.4 Protocole de comparaison
Le protocole de comparaison permet de définir l’ordre sur les éléments d’une collection pour
certaines opérations comme, typiquement, le tri des éléments.
La définition d’une classe fille de Comparer permet la comparaison d’instances, par exemple
pour trier les objets. Cette classe Comparer contient une méthode abstraite : la méthode de
comparaison, qui doit être implémentée. La classe Comparer est générique (elle s’applique à
un template).
Dans l’exemple suivant, on compare des dates (de type MyDate) pour les trier dans l’ordre
chronologique.
CoursLinq/ExemplesDelgate/ex09_MyDateComparer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using
using
using
using
using
using
System ;
System . C o l l e c t i o n s ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
// L ’ i m p l é m e n t a t i o n de l ’ i n t e r f a c e IComparer permet
// l a comparaison d ’ i n s t a n c e s , par exemple pour t r i e r l e s o b j e t s
public c l a s s MyDateComparer : Comparer<MyDate>
{
public override int Compare ( MyDate a , MyDate b )
{
38
Chapitre 3 : Collections
17
18
19
20
return S t r i n g . Compare ( a . T o S t r i n g R e v e r s e ( ) , b . T o S t r i n g R e v e r s e ( ) ) ;
}
}
}
Voici un exemple d’utilisation pour trier une liste de dates avec la méthode List<T>.Sort.
CoursLinq/ExemplesDelgate/ex10_TestComparerClass.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s TestComparerClass
{
public s t a t i c void T e s t S o r t D a t e s ( )
{
L i s t <MyDate> l i s t e D a t e s = new L i s t <MyDate>() ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2012 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2015 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/10/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 10/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2013 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/08/2015 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 20/09/2014 ” ) ) ;
l i s t e D a t e s . Add(new MyDate ( ” 12/09/2013 ” ) ) ;
l i s t e D a t e s . S o r t (new MyDateComparer ( ) ) ;
foreach ( MyDate d a t e in l i s t e D a t e s )
C on s o l e . Write ( d a t e + ” , ” ) ;
C o n s o le . WriteLine ( ) ;
}
}
}
3.5 Protocole d’égalité
Le protocole d’égalité permet de définir les critères sur les éléments d’une collection pour les
considérer comme égaux, ou plus exactement, comme équivalents. En effet, par défaut, les
références des instances de classes sont utilisées pour tester l’égalité. Ça n’est pas toujours
pertinent.
Si l’on reprend l’exemple de notre classe MyDate de la partie 2.1, deux instances différentes
(donc avec des références différentes) de la classe MyDate pourraient représenter la même date
si les ont même année, même mois, et même jour. Le programmeur doit ainsi spécifier les
critères à prendre en compte pour l’égalité, ce qui se définit par une fonction booléenne.
L’implémentation d’une classe dérivée de la classe EqualityComparer permet la comparaison
d’instances pour tester l’égalité (ou l’équivalence) des objets. Celà nécessite l’implémentation
39
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
de deux méthodes (qui sont virtuelles au nniveau de la classe EqualityComparer) : la méthode
booléenne d’égalité Equals, et la méthode GetHashCode qui donne le code de hachage. La
classe est générique (elle s’applique à un template).
Dans l’exemple suivant, on compare des dates (de type MyDate) pour définir s’il s’agit
“d’une même date”.
CoursLinq/ExemplesDelgate/ex11_MyDateEqualityComparer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
c l a s s MyDateEqualityComparer : EqualityComparer<MyDate>
{
public override bool Equals ( MyDate d1 , MyDate d2 )
{
return d1 . Annee == d2 . Annee && d1 . Mois == d2 . Mois && d1 . Jour == d2 .
Jour ;
}
public override int GetHashCode ( MyDate d )
{
// Chaque d a t e c o r r e s p o n d r a à un e n t i e r u n i q u e ( c ’ e s t l e mieux )
return int . Parse ( d . Annee . T o S tr i n g ( ) . PadLeft ( 4 , ’ 0 ’ ) *10000 + d . Mois .
T o S tr i n g ( ) . PadLeft ( 2 , ’ 0 ’ ) *100 + d . Jour . T o S t r i n g ( ) . PadLeft ( 2 ) ) ;
}
}
}
Dans l’exemple suivant, on voit comment utiliser ce protocole d’égalité pour gérer l’interdiction des doublons dans la collection des clefs d’un dictionnaire (chaque clef est unique, mais
on ne parle pas forcément de l’égalité des références).
Dans notre cas des dates, si on veut associer à chaque date une liste d’événements (ou
autre), il s’agit que les événements d’une même date soient regroupés dans le dictionnaire et
acessible avec la clef correspondant à cette date.
CoursLinq/ExemplesDelgate/ex12_TestEqualityComparer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
using
using
using
using
using
System ;
System . C o l l e c t i o n s . G e n e r i c ;
System . Linq ;
System . Text ;
System . Threading . Tasks ;
namespace ExemplesDelegate
{
public c l a s s T e s t E q u a l i t y C o m p a r e r C l a s s
{
s t a t i c void TryAdd ( D i c t i o n a r y <MyDate , string> d i c o , MyDate date , string
chaine )
{
40
Chapitre 3 : Collections
14
15
16
17
18
19
20
try
{
d i c o . Add( date , c h a i n e ) ;
} catch
{
i f ( c h a i n e != null )
C on s o le . E r r o r . WriteLine ( ” T e n t a t i v e d ’ a j o u t d ’ une c l é
e x i s t a n t e dans l e d i c o . ” ) ;
}
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
}
public s t a t i c void T e s t I n s e r t D a t e s ( )
{
D i c t i o n a r y <MyDate , string> d i c o = new D i c t i o n a r y <MyDate , string >(new
MyDateEqualityComparer ( ) ) ;
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
TryAdd ( d i c o
,
,
,
,
,
,
,
,
,
new
new
new
new
new
new
new
new
new
MyDate ( ” 20/09/2014 ” )
MyDate ( ” 20/09/2012 ” )
MyDate ( ” 20/09/2015 ” )
MyDate ( ” 20/10/2014 ” )
MyDate ( ” 10/09/2014 ” )
MyDate ( ” 20/09/2015 ” )
MyDate ( ” 20/08/2015 ” )
MyDate ( ” 20/09/2014 ” )
MyDate ( ” 12/09/2013 ” )
,
,
,
,
,
,
,
,
,
foreach ( string v a l e u r in d i c o . Values )
C on s o l e . Write ( v a l e u r + ” , ” ) ;
C o n s o le . WriteLine ( ) ;
}
}
}
41
”1 è r e
”2ème
”3ème
”4ème
”5ème
”6ème
”7ème
”8ème
”9ème
date
date
date
date
date
date
date
date
date
20/09/2014 ” ) ;
20/09/2012 ” ) ;
20/09/2015 ” ) ;
22/10/2014 ” ) ;
10/09/2014 ” ) ;
20/09/2013 ” ) ;
20/08/2015 ” ) ;
20/09/2014 ” ) ;
12/09/2013 ” ) ;
Chapitre 4
Requêtes LINQ
4.1 Types délégués anonymes
Comme nous l’avons vu dans le chapitre 2, les types délégués sont des types dont les instances
peuvent contenir des méthodes. Par exemple, si l’on considère un type délégué pour un prédicat
qui, pour une personne, renvoie un booléen. On pourra par exemple affecter dans une instance
de ce type délégué une méthode qui renvoie vrai si l’âge de la personne est supérieur ou
égal à 18 ans. Le code correspondant serait, en supposant que l’on dispose d’une méthode
Personne.getAge() qui renvoie une nombre d’années révolues depuis la date de naissance de
la personne (instance d’une classe Personne).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
c l a s s DemoDelegate {
// Type d é l é g u é pour d e s p r é d i c a t s s u r d e s p e r s o n n e s :
public delegate bool MonTypePredicatSurPersonne ( Personne p e r s o n n e ) ;
// Méthode q u i t e s t e une p r é d i c a t s u r une Personne
public s t a t i c void T e s t P r e d i c a t P e r s o n n e ( MonTypePerdicatSurPersonne p r e d i c a t )
{
// C r é a t i o n d ’ une i n s t a n c e de Personne ( a p p e l de f a b r i q u e )
Personne p e r s o n n e = Personne . f a b r i q u e D e P e r s o n n e ( ) ;
i f ( pr e d i c a t ( personne ) )
{
C o n s o l e . WriteLine ( ”La pe rs o n n e v é r i f i e l e p r é d i c a t . ” ) ;
} else
{
C o n s o l e . WriteLine ( ”La pe rs o n n e v é r i f i e l e p r é d i c a t . ” ) ;
}
}
public s t a t i c A p p e l l e T e s t P r e d i c a t P e r s o n n e ( )
{
T e s t P r e d i c a t P e r s o n n e ( ( Personne p e r s o n n e ) => {
return p e r s o n n e . GetAge ( ) >= 1 8 ;
}) ;
}
}
Avec cette syntaxe pour définir des types délégués, nous devons obligatoirement définir
notre type délégué et lui donner un nom avant de l’utiliser pour définir des fonctions. En fait,
42
Chapitre 4 : Requêtes LINQ
il existe en C#une syntaxe pour définir des types délégués à la volée.
Reprenons notre exemple, ci-dessus, du prédicat qui, pour une personne, renvoie un booléen. Nous aurions pu définir notre méthode TestPredicatPersonne directement sans définir
auparavant un type MonTypePredicatSurPersonne :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
c l a s s DemoDelegate {
// Méthode q u i t e s t e une p r é d i c a t s u r une Personne
public s t a t i c void T e s t P r e d i c a t P e r s o n n e ( Fonc<Personne , bool> p r e d i c a t )
{
// C r é a t i o n d ’ une i n s t a n c e de Personne ( a p p e l de f a b r i q u e )
Personne p e r s o n n e = Personne . f a b r i q u e D e P e r s o n n e ( ) ;
i f ( pr e d i c a t ( personne ) )
{
Co n s o l e . WriteLine ( ”La pe r so n n e v é r i f i e l e p r é d i c a t . ” ) ;
} else
{
C o n s o l e . WriteLine ( ”La pe rs o n n e v é r i f i e l e p r é d i c a t . ” ) ;
}
}
public s t a t i c A p p e l l e T e s t P r e d i c a t P e r s o n n e ( )
{
T e s t P r e d i c a t P e r s o n n e ( ( Personne p e r s o n n e ) => {
return p e r s o n n e . GetAge ( ) >= 1 8 ;
}) ;
}
}
Ainsi, Fonc<Personne,bool> désigne un type déléggué qui à une Personne associe un
booléen (prédicat sur une Personne). De même, Fonc<Personne,int> désigne un type déléggué
qui à une Personne associe un entier.
4.2 Méthodes d’extension
Les méthodes d’extension permettent d’ajouter des méthodes à une classe existante sans retoucher le code de la classe proprement dite. Les méthodes de LINQ sont définies comme des
méthodes d’extension sur les différentes classes de collections. Ça n’est pas difficile à utiliser,
mais pour compreendre les prototypes des méthodes de LINQ, il faut savoir lire le prototype
d’une méythode d”extension.
Par exemple, supposons que nous souhaitions ajouter à la classe string une méthode qui
calcule le nombre d’occurences d’un caractère passé en paramètre. Voici comme on peut faire :
1
2
3
4
5
6
7
8
using System ;
using System . Linq ;
using System . Text ;
namespace DemoExtension
{
// Les méthodes d ’ e x t e n s i o n d o i v e n t ê t r e d é f i n i e s dans une c l a s s e s t a t i q u e
public s t a t i c c l a s s S t r i n g E x t e n s i o n
43
Rémy Malgouyres, www.malgouyres.org
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Conception Objet et Programmation en C#
{
// Ceci e s t l a méthode d ’ e x t e n s i o n .
// Le p r em ie r paramètre e s t d é c l a r é a v e c l e m o d i f i c c a t e u r t h i s
// La méthode s ’ a p p l i q u e r a à d e s i n s t a n c e s de l a c l a s s e
// c o r r e s p o n d a n t au premie r paramètre ( i c i S t r i n g ) .
public s t a t i c int CompteOccurences ( t h i s S t r i n g c h a i n e , Char c a r a c t e r e )
{
// La méthode Where s é l e c t i o n n e l e s c a r a c t è r e s s u i v a n t un p r e d i c a t
// ( v o i r d é t a i l s de l a méthode c i −d e s s o u s
return c h a i n e . Where ( ( Char c ) => { return c==c a r a c t e r e ; } )
. ToList ( ) . Count ( ) ;
}
}
c l a s s Program
{
s t a t i c void Main ( string [ ] a r g s )
{
string s = ”C# p e u t r e p r é s e n t e r un paradigme du l a n g a g e o b j e t ” ;
// Appel de l a méthode d ’ e x t e n s i o n q u i compte l e s
// o c c u r e n c e s de l a l e t t r e ’ a ’ dans l a c h a i n e s
int nOccurences = s . CompteOccurences ( ’ a ’ ) ;
C o n s o le . WriteLine ( ” I l y a {0} c a r a c t è r e s {1} dans l a c h a i n e {2} ” ,
nOccurences , ’ a ’ , s ) ;
}
}
}
Notez que le premier paramètre dans la définition de la méthode d’extention, qui
est modifié par le mot clé this, ne fait pas vraiment partie des paramètres de
la méthode, mais il représente l’instance de classe sur laquellem s’appliquera la
méthode.
4.3 Interface IEnumerable<T>
L’interface IEnumerable<T> est l’interface de base de toutes les classes de collecctions génériques. Elle ne contient qu’une seule méthode, qui renvoie un énumérateur (aussi appelé
itérateur).
1
IEnumerator<T> GetEnumerator ( )
L’énumérateur possède les méthodes qui permettent de parcourir une collection (se positionner au début, obtenir l’élément suivant, etc.). L’interface IEnumerable<T> esst ainsi le
strict nécesssaire pour parcourir une collection.
La plupart des méthodes de LINQ, qui s’appliquent à une collection renvoient une collection
en sortie, sont des méthodes d’extension de l’interface IEnumerable<T>. Elles auront donc un
premier paramètre this IEnumerable<T> :
1
IEnumerable<T> MaMethodeDeLINQ ( t h i s IEnumerable<T>, e t c . . . )
44
Chapitre 4 : Requêtes LINQ
4.4 Quelques méthodes de LINQ
4.4.1 Filtre Where
La méthode d’extension Where, qui est définie sur les principaux types de collections en C#.
1
2
3
4
public s t a t i c IEnumerable<TSource> Where<TSource >(
t h i s IEnumerable<TSource> s o u r c e ,
Func<TSource , bool> p r e d i c a t e
)
Paramètres :
• source : une collection générique qui implémente IEnumerable<TSource>, dont les éléments sont d’un type TSource.
• predicate : Prédicat sur le type TSource des éléments de la collection source (qui à une
instance de TSource associe un booléen.
Valeur retournée : La sous collection (du type IEnumerable<TSource>) de la collection
source formée des éléments qui satisfont le prédicat (le prédicat renvoie vrai sur ces éléments).
4.4.2 Méthode de tri OrderBy
La méthode d’extension OrderBy, qui est définie sur les principaux types de collections en
C#, permet de trier les éléments suivant un critère de tri (implémentation de IComparer (voir
partie 3.4).
1
2
3
4
5
public s t a t i c IOrderedEnumerable<TSource> OrderBy<TSource , TKey>(
t h i s IEnumerable<TSource> s o u r c e ,
Func<TSource , TKey> k e y S e l e c t o r ,
IComparer<TKey> comparer
)
Paramètres :
• source : une collection générique qui implémente IEnumerable<TSource>, dont les éléments sont d’un type TSource.
• keySelector : Sélection de clef sur le type TSource des éléments de la collection source
(qui à une instance de TSource associe une clef de type TKey.
• comparer : instance d’une classe de comparaison des clefs (qui implémente l’interface
IComparer). Si le paramètre comparer est omis, le comparateur par défaut sur les clefs,
s’il existe, est utilisé.
Valeur retournée : La collection (du type IEnumerable<TSource>) qui comprend les mêmes
éléments que la collection source, mais triés suivant l’ordre défini par la méthode Compare du
comparer.
45
Rémy Malgouyres, www.malgouyres.org
Conception Objet et Programmation en C#
4.4.3 Filtre Where sur un dictionnaire
La méthode d’extension Where, qui est définie sur les principaux types de collections en C#.
1
2
3
4
public s t a t i c IEnumerable<KeyValuePair<TKey , TValue>> Where<KeyValuePair<TKey ,
TValue>>(
t h i s IEnumerable<KeyValuePair<TKey , TValue>> s o u r c e ,
Func<KeyValuePair<TKey , TValue >, Boolean> p r e d i c a t e
)
Paramètres :
• source : un dictionnaire qui implémente IDictionnary<TKey,TValue>, dont les clef sont
d’un type TKey et les valeurs d’un type TValue,
• predicate : Prédicat sur le type KeyValuePair<TKey, TValue> des éléments de la collection source (qui à une instance de KeyValuePair<TKey, TValue> associe un booléen.
Valeur retournée : La sous collection (du type KeyValuePair<TKey, TValue>) des couple
clef/valeur du dictionnaire source formée des couples qui satisfont le prédicat (le prédicat
renvoie vrai sur ces éléments).
4.4.4 Méthode de regrouppement GroupBy
La méthode d’extension GroupBy, qui est définie sur les principaux types de collections en C#,
permet de regroupe les éléments suivant un critère de projection et de comparaison (implémentation de IEqualityComparer (voir partie 3.5).
1
2
3
4
5
6
7
8
public s t a t i c IEnumerable<IGrouping<TKey , TSource>> GroupBy<TSource , TKey>(
t h i s IEnumerable<TSource> s o u r c e ,
Func<TSource , TKey> k e y S e l e c t o r ,
IEqualityComparer<TKey> comparer
)
public i n t e r f a c e IGrouping<out TKey , out TElement> : IEnumerable<TElement >,
IEnumerable
Paramètres :
• source : une collection générique qui implémente IEnumerable<TSource>, dont les éléments sont d’un type TSource.
• keySelector : Sélection de clef sur le type TSource des éléments de la collection source
(qui à une instance de TSource associe une clef de type TKey).
• comparer : instance d’une classe de comparaison des clefs (qui implémente l’interface
IEqualityComparer) (voir partie 3.5). Si le paramètre comparer est omis, le comparateur
d’égalité par défaut sur les clefs, s’il existe, est utilisé.
Valeur retournée : L’interface IGrouping<out TKey, out TElement>, similaire à un dictionnaire, représente une association qui à chaque clef unique (suivant le protocole d’égalité
défini par comparer) associe la collection (du type IEnumerable<TSource>) qui comprend les
éléments que la collection source qui possèdent cette clef.
On peut parcourir la collection regroupée par une double boucl foreach imbriquée.
46
Chapitre 4 : Requêtes LINQ
4.4.5 Exemples
Voici le diagramme d’une classe personne, avec un nom, un prénom et une date de naissance.
Une méthode GetAge permet d’obtenir l’âge de la personne en années révolues (au format int).
Diag 8. Diagramme de Classes : la classe
Personne.
Voici maintenant des exemples sélectionnant, dans une liste de personnes, les personnes
majeures, triées et/ou regroupées suivant différents critères.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System ;
using System . C o l l e c t i o n s . G e n e r i c ;
using System . Linq ;
namespace ExemplesLINQ
{
public c l a s s ExemplesPersonnesLINQ
{
// L ’ i m p l é m e n t a t i o n de l ’ i n t e r f a c e IComparer permet
// l a comparaison d ’ i n s t a n c e s , par exemple pour t r i e r l e s o b j e t s
public c l a s s MyDateComparer : Comparer<MyDate>
{
public override int Compare ( MyDate a , MyDate b )
{
return S t r i n g . Compare ( a . T o S t r i n g R e v e r s e ( ) , b . T o S t r i n g R e v e r s e ( ) ) ;
}
}
// L ’ h é r i t a g e de l a c l a s s e EqualityComparer<MyDate> permet
// l a comparaison d ’ i n s t a n c e s pour t e s t e r l ’ é g a l i t é
// Ou p l u t ô t , l ’ é q u i v a l e n c e , s u i v a n t un c e r t a i n c r i t è r e , i c i l ’ année .
c l a s s MyDateYearEquality : EqualityComparer<MyDate>
{
public override bool Equals ( MyDate d1 , MyDate d2 )
{
return d1 . Annee == d2 . Annee ;
}
public override int GetHashCode ( MyDate d )
{
return int . Parse ( d . Annee ) ;
}
}
47
Rémy Malgouyres, www.malgouyres.org
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Méthode de d é m o n s t r a t i o n d ’ e x e m p l e s LINQ s u r l e s Personne
public s t a t i c L i s t <Personne> DemoLINQ_Personne1 ( L i s t <Personne> l i s t e )
{
// Exemple a v e c comparat eu rs de s t r i n g par d é f a u t
// ( o r d r e a l p h a b é t i q u e )
return l i s t e . Where ( ( Personne p ) => { return ( p . GetAge ( ) >= 1 8 ; } ) )
. OrderBy ( ( Personne p ) => { return p .Nom; } )
. ThenBy ( ( Personne p ) => { return p . Prenom ; } ) . ToList ( ) ;
}
// Méthode de d é m o n s t r a t i o n d ’ e x e m p l e s LINQ s u r l e s Personne
public s t a t i c L i s t <Personne> DemoLINQ_Personne2 ( L i s t <Personne> l i s t e )
{
// Même exemple a v e c s y n t a x e d ’ e x p r e s s i o n lambda p l u s l é g è r e
// Types d e s p a r a m è t r e s i m p l i c i t e s , c o r p s de f o n c t i o n r é d u i t
return l i s t e . Where ( p => ( p . GetAge ( ) >= 1 8 ) )
. OrderBy ( p => p .Nom)
. ThenBy ( p => p . Prenom ) . ToList ( ) ;
}
// Méthode de d é m o n s t r a t i o n d ’ e x e m p l e s LINQ s u r l e s Personne
public s t a t i c L i s t <Personne> DemoLINQ_Personne3 ( L i s t <Personne> l i s t e )
{
// Exemple a v e c c omp ar a te u r s de MyDate par o r d r e
chronologique
// Voir p a r t i e 3 . 4 du p o l y ( p r o t o c o l e de comparaison )
return l i s t e . Where ( p => { return ( p . GetAge ( ) >= 1 8 ; } ) )
. OrderBy ( p => p . DateNaissance , new MyDateComparer ( ) ) ;
}
58
59
60
61
62
63
64
65
// Méthode de d é m o n s t r a t i o n d ’ e x e m p l e s LINQ s u r l e s Personne
public s t a t i c L i s t <Personne> DemoLINQ_Personne4 ( L i s t <Personne> l i s t e )
{
// Exemple a v e c c omp ar a te u r s de MyDate par o r d r e
chronologique
// Exemple a v e c comparat eu rs de MyDate par o r d r e c h r o n o l o g i q u e
// Voir p a r t i e 3 . 4 du p o l y ( p r o t o c o l e de comparaison )
return l i s t e . Where ( p => { return ( p . GetAge ( ) >= 1 8 ; } ) )
. OrderBy ( p => p . DateNaissance , new MyDateComparer ( ) ) ;
. GroupBy ( p => p . DateNaissance , new MyDateYearEquality ( ) )
;
}
66
67
68
69
70
71
72
73
Conception Objet et Programmation en C#
}
}
48