Introduction à la Programmation C# et .NET
Introduction à la Programmation C# et .NET
com
Programmation en C#
SUPINFO DOT NET TRAINING COURSE
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 3 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 4 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 5 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 6 / 83
La plate-forme .NET prend totalement en charge les technologies Internet basées sur les normes et
indépendantes des plates-formes, telles que http (Hypertext Transfer Protocol), XML (Extensible
Markup Language) et SOAP (Simple Object Access Protocol).
La plate-forme .NET offre plusieurs technologies de base. Ces technologies sont décrites dans les
rubriques suivantes.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 7 / 83
1.2.6. [Link]
[Link] est la nouvelle génération de la technologie ADO (ActiveX® Data Object) de Microsoft.
Elle fournit une prise en charge améliorée du modèle de programmation déconnecté, ainsi qu'une prise
en charge riche du XML.
1.2.7. [Link]
Microsoft [Link] est une structure de programmation fondée sur le Common Language Runtime. Il
peut être employé sur un serveur pour créer des applications Web puissantes. Les formulaires Web
[Link] sont des outils puissants et faciles d'emploi permettant de créer des interfaces utilisateur
Web dynamiques.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 8 / 83
1.2.10. Langages
Tout langage conforme à la norme CLS (Common Language Specification) peut fonctionner dans le
Common Language Runtime. Dans le .NET Framework, Microsoft prend en charge Visual Basic,
Visual C++, Microsoft Visual C#, Visual J# et Microsoft JScript®. Les fournisseurs indépendants
peuvent proposer d'autres langages.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 9 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 10 / 83
Composant Description
Gère les métadonnées, en plus du chargement des classes et de leur
Chargeur de classes
disposition.
Compilateur MSIL Convertit MSIL en code natif (juste-à-temps).
(Microsoft Intermediate
Language) en code natif
Gestionnaire de code Gère l'exécution du code.
Fournit une gestion automatique de la durée de vie de tous vos objets.
Garbage collector (GC)
Il s'agit d'un mécanisme multiprocesseur et évolutif.
Fournit une sécurité par preuve, fondée sur l'origine du code en plus
Moteur de sécurité
de l'utilisateur.
Moteur de débogage Permet de déboguer l'application et de tracer l'exécution du code.
N'autorise pas les conversions non sécurisées ou les variables non
Vérificateur de type initialisées. Le langage MSIL peut être vérifié pour garantir la
sécurité de type.
Fournit un traitement structuré des exceptions, intégré à SEH
Gestionnaire d'exceptions (WindowsStructured Exception Handling). La génération de rapports
d'erreurs a été améliorée.
Fournit des classes et des interfaces qui permettent la programmation
Prise en charge multithread
multithread.
Marshaleur COM Fournit le marshaling à partir et à destination de COM.
Prise en charge de la Intègre du code au runtime qui prend en charge la BCL.
bibliothèque de classes de
base (BCL)
Tableau 1 - Description des composants
L'espace de noms [Link] fournit des listes triées, des tables de hachage et d'autres
méthodes de regroupement de données. L'espace de noms [Link] fournit des entrées/sorties (E/S),
des flux, etc. L'espace de noms [Link] offre une prise en charge des sockets et de TCP/IP
(Transmission Control Protocol/Internet Protocol).
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 11 / 83
Le Common Language Runtime fournit un support intégré pour la création et l'exposition de services
Web XML, grâce à l'emploi d'une abstraction de programmation cohérente et familière à la fois pour
les développeurs de formulaires Web ASP (Active Server Pages) et Visual Basic. Le modèle résultant
est à la fois évolutif et extensible. Ce modèle reposant sur des standards Internet ouverts (HTTP,
XML, SOAP et SDL), tout ordinateur client ou périphérique Internet peut y accéder et l'interpréter.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 12 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 13 / 83
Langage Description
C# a été conçu pour la plate-forme .NET. Il s'agit du premier langage
moderne orienté composant dans la famille C et C++. Il peut être
incorporé dans des pages [Link].
Certaines de ses principales fonctionnalités sont : les classes, les
C# interfaces, les délégués, le boxing et l'unboxing, les espaces de noms,
les propriétés, les indexeurs, les événements, la surcharge d'opérateurs,
la gestion de versions, les attributs, le code non sécurisé et la génération
de documents XML. Aucun en-tête ou fichier IDL (Interface Definition
Language) n'est nécessaire.
Le langage C++ managé est une extension minimale du langage C++.
Cette extension fournit un accès au .NET Framework qui comprend le
Extensions managées pour garbage collection, l'héritage d'implémentation simple et l'héritage
C++ d'interface multiple. Cette mise à jour dispense également le
développeur d'écrire du code de raccord pour les composants. Il offre
un accès de bas niveau si besoin est.
Visual Basic .NET présente diverses innovations importantes par
rapport aux versions précédentes de Visual Basic. Visual Basic .NET
prend en charge l'héritage, les constructeurs, le polymorphisme, la
surcharge du constructeur, les exceptions structurées, un contrôle de
type plus strict, des modèles de thread libres ainsi que de nombreuses
Visual Basic .NET autres fonctionnalités. Il existe une seule forme d'assignation : aucune
méthode Let ou Set. Les nouvelles fonctions de développement rapide
d'application, telles que le Concepteur XML, l'Explorateur de serveurs
et le Concepteur Web Forms, sont disponibles dans Visual Basic à
partir de Visual Studio .NET. Avec cette version, VBScript offre une
fonctionnalité Visual Basic complète.
JScript .NET est réécrit pour prendre entièrement en charge .NET. Il
prend en charge les classes, l'héritage, les types et la compilation, et
comprend des fonctionnalités de performance et de productivité
JScript .NET
optimisées. JScript .NET est également intégré avec Visual Studio
.NET. Vous pouvez utiliser n'importe quelle classe .NET Framework
dans JScript .NET.
Visual J# .NET est un outil de développement Java destiné aux
développeurs Java qui souhaitent créer des applications et des services
sur le .NET Framework. Visual J# .NET est entièrement compatible
avec .NET et inclut des outils pour mettre à jour et convertir
Visual J# .NET
automatiquement les projets et les solutions Visual J++ 6.0 existants au
nouveau format Visual Studio .NET. Visual J# .NET fait partie de la
stratégie de transition de Java à .NET (Java User Migration Path to
Microsoft .NET, « JUMP to .NET »).
Divers langages tiers prennent en charge le .NET Framework. Citons,
Langages tiers par exemple, APL, COBOL, Pascal, Eiffel, Haskell, ML, Oberon, Perl,
Python, Scheme et SmallTalk.
Tableau 2 - Description des langages
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 14 / 83
2. Vue d’ensemble du C#
2.1. Structure du programme C#
Dans cette leçon, vous allez apprendre la structure de base d'un programme C#.
Vous allez analyser un programme simple contenant toutes les fonctionnalités essentielles. Vous
apprendrez également à utiliser Microsoft® Visual Studio® pour créer et modifier un programme C#.
L'exemple de code de la diapositive contient tous les éléments essentiels d'un programme C#, et il est
facile à tester ! Lorsqu'il est exécuté depuis la ligne de commandes, il affiche simplement :
Hello, World
Dans les rubriques suivantes, vous allez analyser ce programme simple pour découvrir plus en détail
les blocs de construction d'un programme C#.
2.1.2. La classe
Dans C#, une application est une collection d'une ou plusieurs classes, structures de données et autres
types. Dans ce module, une classe est définie comme un ensemble de données associé à des méthodes
(ou fonctions) qui peuvent manipuler ces données. Dans les modules suivants, vous étudierez les
classes et tout ce qu'elles offrent aux programmeurs en C#.
Si vous étudiez le code de l'application « Hello, World », vous verrez une seule classe appelée Hello.
Cette classe est introduite par le mot-clé class. Le nom de la classe est suivi d'une accolade ouvrante
({). Tous les éléments compris entre cette accolade ouvrante et l'accolade fermée (}) font partie de la
classe.
Vous pouvez répartir les classes d'une application C# dans un ou plusieurs fichiers. Vous pouvez
placer plusieurs classes dans un fichier, mais vous ne pouvez pas étendre une même classe sur
plusieurs fichiers.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 15 / 83
Le langage C# respecte la casse. Main doit être écrit avec un « M » majuscule et le reste du mot en
minuscules.
Une application C# peut contenir plusieurs classes, mais un seul point d'entrée. Vous pouvez avoir
plusieurs classes avec Main dans la même application, mais une seule méthode Main est exécutée.
Vous devez spécifier la classe à utiliser lors de la compilation de l'application.
La signature de la méthode Main est également importante. Si vous utilisez Visual Studio, cette
signature est créée automatiquement en tant que static void. (Vous découvrirez ce que cela signifie
plus loin dans ce cours.) À moins d'avoir une bonne raison de le faire, vous ne devez pas modifier la
signature.
Vous pouvez modifier la signature dans une certaine mesure, mais elle doit toujours être statique ;
sinon, elle peut ne pas être reconnue par le compilateur comme point d'entrée de l'application.
L'application s'exécute jusqu'à la fin de la méthode Main ou jusqu'à ce qu'une instruction return soit
exécutée par la méthode Main.
Le .NET Framework se compose de nombreux espaces de noms, le plus important étant System.
L'espace de noms System contient les classes que la plupart des applications utilisent pour interagir
avec le système d'exploitation. Les classes les plus fréquemment utilisées gèrent les entrées/sorties
(E/S). Comme de nombreux autres langages, C# n'a pas de capacité d'E/S à part entière ; il dépend par
conséquent du système d'exploitation pour fournir une interface compatible avec C#.
Vous pouvez faire référence à des objets d'un espace de noms en les faisant précéder explicitement de
l'identificateur de cet espace de noms. Par exemple, l'espace de noms System contient la classe
Console, qui comporte plusieurs méthodes, telles que WriteLine. Vous pouvez accéder à la méthode
WriteLine de la classe Console de la manière suivante :
[Link]("Hello, World!");
Toutefois, l'utilisation d'un nom qualifié complet pour faire référence aux objets est dangereuse et
risque de provoquer des erreurs. Afin d'éviter ce problème, vous pouvez spécifier un espace de noms
en plaçant une directive using au début de votre application avant la définition de la première classe.
Une directive using spécifie un espace de noms qui sera étudié si une classe n'est pas explicitement
définie dans l'application. Vous pouvez intégrer plusieurs directives using dans le fichier source, mais
elles doivent toutes être placées au début de ce fichier.
Avec la directive using, vous pouvez réécrire le code précédent de la manière suivante :
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 16 / 83
Dans l'application « Hello, World », la classe Console n'est pas explicitement définie. Lors de la
compilation de l'application « Hello, World », le compilateur recherche la classe Console et la trouve
dans l'espace de noms System.
Le compilateur génère du code qui fait référence au nom qualifié complet [Link].
L'entrée standard est normalement associée au clavier ; tout ce que l'utilisateur saisit à partir du clavier
peut être lu à partir du flux d'entrée standard. Le flux de sortie standard et le flux d'erreur standard sont
quant à eux généralement associés à l'écran.
Ces deux méthodes sont surchargées. Vous pouvez les appeler avec différents nombres et types de
paramètres. Par exemple, vous pouvez utiliser le code suivant pour écrire « 99 » à l'écran :
[Link](99);
Vous pouvez utiliser le code suivant pour écrire le message « Hello, World » à l'écran :
[Link]("Hello, World!");
La méthode Read lit le caractère suivant à partir du clavier. Elle renvoie la valeur int –1 si aucune
autre entrée n'est disponible. Sinon, elle renvoie une valeur int représentant le caractère lu.
La méthode ReadLine lit tous les caractères jusqu'à la fin de la ligne d'entrée (le retour chariot).
L'entrée est renvoyée en tant que chaîne de caractères. Vous pouvez utiliser le code ci-dessous pour
lire une ligne de texte à partir du clavier et l'afficher à l'écran :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 17 / 83
C# permet d'ajouter des commentaires au code des applications de plusieurs manières : commentaires
sur une seule ligne, commentaires sur plusieurs lignes ou documentation générée au format XML.
Vous pouvez ajouter un commentaire sur une seule ligne en utilisant les barres obliques (//). Lors de
l'exécution de votre application, tout ce qui suit ces deux caractères jusqu'à la fin de la ligne est ignoré.
Vous pouvez également utiliser des commentaires de bloc qui couvrent plusieurs lignes. Ce type de
commentaires commence par la paire de caractères
/* et continue jusqu'à la paire de caractères */ correspondante. Vous ne
pouvez pas imbriquer de commentaires de bloc.
Les commentaires des documentations commencent par trois barres obliques (///) suivies d'une balise
de documentation XML. Pour consulter des exemples, reportez-vous à la diapositive.
Vous pouvez utiliser les balises XML qui vous sont proposées, ou créer les vôtres. Le tableau suivant
contient certaines balises XML et leur utilisation.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 18 / 83
Balise Rôle
Fournir une brève description. Utilisez la balise <remarks> pour une description
<summary>…</summary>
plus longue.
Fournir une description détaillée. Cette balise peut contenir des paragraphes
<remarks> … </remarks>
imbriqués, des listes et d'autres types de balises.
Ajouter une structure à la description dans une balise <remarks>. Cette balise
<para> … </para>
permet de délimiter des paragraphes.
Ajouter une liste structurée à une description détaillée. Les types de listes pris en
charge sont « bullet » (à puce), « number » (numérotée) et « table » (tableau).
<list type="…"> … </list>
Des balises supplémentaires (<term> … </term> et <description> …
</description>) sont utilisées au sein de la liste pour mieux définir la structure.
Fournir un exemple de l'utilisation d'une méthode, d'une propriété ou d'un autre
<example> … </example> membre de la bibliothèque. Cela implique souvent l'utilisation d'une balise
<code> imbriquée.
<code> … </code> Indiquer que le texte joint est un code d'application.
Indiquer que le texte joint est un code d'application. La balise <code> est utilisée
<c> … </c> pour les lignes de code qui doivent être séparées de toute description jointe ; la
balise <c> est utilisée pour le code incorporé à une description jointe.
Indiquer la référence à un autre membre ou champ. Le compilateur vérifie que ce
<see cref="member"/> « membre » existe réellement.
Indiquer la référence à un autre membre ou champ. Le compilateur vérifie que ce
« membre » existe réellement. La différence entre les balises <see> et <seealso>
<seealso cref="member"/> dépend du processeur qui manipule le document XML une fois ce dernier généré.
Le processeur doit pouvoir générer les sections See (Voir) et See Also (Voir
aussi) pour que ces deux balises soient correctement différenciées.
<exception> … </exception> Fournir une description pour une classe d'exception.
<permission> … Documenter l'accessibilité d'un membre.
</permission>
<param name="name"> … Fournir une description pour un paramètre de méthode.
</param>
<returns> … </returns> Documenter la valeur renvoyée et le type de méthode.
<value> … </value> Décrire une propriété.
Tableau 3 - Balises XML de la documentation
Vous pouvez compiler les balises XML et la documentation dans un fichier XML en utilisant le
compilateur C# avec l'option /doc :
S'il n'y a pas d'erreur, vous pouvez visualiser le fichier XML généré à l'aide d'un outil tel qu'Internet
Explorer.
Le rôle de l'option /doc consiste uniquement à générer un fichier XML. Pour afficher le fichier, vous
avez besoin d'autre processeur. L'affichage du fichier sous Internet Explorer est simple, se limitant à
rendre sa structure ; il permet le développement ou la réduction des balises, mais il n'affiche pas la
balise <list type="bullet"> en tant que puce.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 19 / 83
Exemple :
Si une erreur d'exécution se produit dans une application C#, le système d'exploitation renvoie une
exception. Interceptez les exceptions à l'aide d'une construction try-catch, comme illustré sur la
diapositive. Si l'une des instructions de la partie try de l'application provoque une exception,
l'exécution est transférée au bloc catch.
Vous pouvez rechercher des informations sur l'exception à l'aide des propriétés StackTrace, Message
et Source de l'objet Exception. Vous en apprendrez davantage sur la gestion des exceptions dans un
module ultérieur.
Si vous imprimez une exception, à l'aide de la méthode [Link] par exemple, l'exception
est automatiquement formatée et affiche les propriétés StackTrace, Message et Source.
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 20 / 83
Plus précisément, les applications C# sont compilées en langage MSIL (Microsoft Intermediate
Language) plutôt qu'en code machine natif.
Le code MSIL est lui-même compilé en code machine par le compilateur juste-à-temps (JIT) lors de
l'exécution de l'application. Toutefois, il est également possible de compiler directement en code
machine et d'ignorer le compilateur juste-à-temps en utilisant l'utilitaire Native Image Generator
([Link]).
Cet utilitaire crée une image native à partir d'un assembly managé et l'installe dans le cache d'images
natives sur l'ordinateur local. L'exécution de [Link] sur un assembly permet de charger celui-ci plus
rapidement, car ce programme restaure les structures de code et de données à partir du cache d'images
natives au lieu de les générer de manière dynamique.
Vérifiez que le fichier de sortie contenant le code compilé est spécifié avec un suffixe .exe. Si ce
suffixe est omis, vous devez renommer le fichier avant de l'exécuter.
Par défaut, Visual Studio ouvre la configuration de débogage des projets. Cela signifie qu'une version
déboguée de l'application est compilée.
Pour compiler une version Release sans informations de débogage, définissez la configuration de
solution sur Release.
2.4.3. Débogage
Exceptions et débogage JIT
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 21 / 83
Si votre application renvoie une exception et que vous n'avez pas écrit de code pouvant gérer cette
exception, le Common Language Runtime déclenche le débogage JIT (à ne pas confondre avec le
compilateur JIT.)
En supposant que vous avez installé Visual Studio, une boîte de dialogue s'affiche pour vous permettre
de déboguer l'application en utilisant le débogueur Visual Studio (environnement de développement
Microsoft) ou le débogueur fourni avec le Kit de développement .NET Framework SDK.
Si vous avez accès à Visual Studio, il est recommandé de sélectionner le débogueur de Microsoft
Development Environment.
La fenêtre Espion est utile pour contrôler la valeur des variables sélectionnées lors de l'exécution de
l'application. Si vous saisissez le nom d'une variable dans la colonne Nom, la valeur correspondante
s'affiche dans la colonne Valeur.
Lors de l'exécution de l'application, toutes les modifications de cette valeur s'affichent. Vous pouvez
également modifier par écrasement la valeur d'une variable surveillée.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 22 / 83
Le système de types communs (CTS) fait partie intégrante du Common Language Runtime. Les
compilateurs, les outils et le Runtime lui-même se partagent ce système. Il est le modèle définissant les
règles suivies par le Runtime pour déclarer, utiliser et gérer les types. Il définit une structure qui
permet l'intégration entre les langages, la sécurité des types et une exécution très performante du code.
Types référence
Les variables de type référence contiennent des références aux données qu'elles possèdent. Leurs
données sont stockées dans un objet. Deux variables de type référence peuvent référencer le même
objet. Les opérations sur une variable de ce type peuvent affecter l'objet référencé par une autre
variable de type référence.
3.1.3. Comparaison des types valeur définis par l’utilisateur et des types
valeur intégrés
Il existe différents types valeur : les types définis par l'utilisateur et les types intégrés. En C#, leur
différence est minime, car les types définis par l'utilisateur peuvent être utilisés de la même façon que
les types intégrés. La seule vraie différence entre les types de données intégrés et les types de données
définis par l'utilisateur réside dans la possibilité d'utiliser des valeurs littérales pour les types intégrés.
Tous les types valeur contiennent des données, et celles-ci ne peuvent pas être null.
Il est impossible de distinguer un type simple du type struct dont il est l'alias. Dans votre code, vous
pouvez utiliser le mot clé réservé ou le type struct. Les exemples suivants illustrent les deux
possibilités :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 23 / 83
// ou
// ou
Le tableau ci-dessus répertorie les mots clés réservés les plus courants et leur type struct équivalent.
Respectez les conventions d'affectation de noms standard recommandées pour C#. Mémorisez les
mots clés réservés en C# que vous ne pouvez pas utiliser comme noms de variables.
Règles
Les points suivants correspondent aux règles d'affectation de noms pour les variables C# :
- un nom de variable doit commencer par une lettre ou un trait de soulignement
- le premier caractère doit être suivi par des lettres, des chiffres ou un trait de soulignement
- les mots clés réservés sont proscrits
- l'utilisation d'un nom de variable non autorisé entraînera une erreur de compilation
Recommandations
Voici les recommandations à suivre pour l'affectation de noms de variables :
- éviter d'utiliser uniquement de lettres majuscules
- éviter les noms de variables commençant par un trait de soulignement
- éviter d'utiliser des abréviations
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 24 / 83
void InitializeData();
int loopCountMax;
Vous trouverez ci-dessous la liste des mots clés de C#. Rappelez-vous que vous ne pouvez pas les
utiliser comme noms de variables.
abstract as base bool break
byte case catch char checked
class const continue decimal default
delegate do double else enum
event explicit extern false finally
fixed float for foreach goto
if implicit in int interface
internal is lock long namespace
new null object operator out
override params private protected public
readonly ref return sbyte sealed
short sizeof stackalloc static string
struct switch this throw true
try typeof uint ulong unchecked
unsafe ushort using virtual void
volatile while
Tableau 5 - Mots clés de C#
int itemCount;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 25 / 83
Vous pouvez déclarer plusieurs variables dans une seule déclaration en utilisant un séparateur de type
virgule, comme illustré dans l'exemple suivant :
En C#, vous ne pouvez pas utiliser de variables non initialisées. Le code suivant entraînera une erreur
de compilation, car aucune valeur initiale n'a été attribuée à la variable loopCount :
int loopCount;
[Link]("{0}", loopCount); // erreur
int employeeNumber;
employeeNumber = 23;
Vous pouvez aussi initialiser une variable lorsque vous la déclarez, comme illustré dans l'exemple
suivant :
Vous pouvez utiliser l'opérateur d'assignation pour attribuer des valeurs aux variables de type
caractère, comme illustré dans l'exemple suivant :
Dans ces cas-là, si vous devez écrire la même expression deux fois, vous pouvez introduire facilement
un bogue subtil. Heureusement, il existe une syntaxe abrégée qui permet d'éviter la duplication :
itemCount += 40;
items[(index + 1) % 32] += 40;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 26 / 83
Certains des opérateurs les plus courants utilisés en C# sont décrits dans le tableau ci-dessous.
Par défaut, les valeurs de l'énumérateur commencent à 0. Dans l'exemple précédent, Red a la valeur 0,
Green la valeur 1 et Blue, la valeur 2.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 27 / 83
[Link]("{0}",colorPalette);
Dans le .NET Framework, tous les types de données simples tels que int, float et double sont des
structures intégrées.
Ce code définit un nouveau type appelé Employee qui se compose de deux éléments :
first name et age.
using System;
class Test
{
static void Main( )
{
int intValue = 123;
long longValue = intValue;
[Link]("(long) {0} = {1}", intValue, longValue);
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 28 / 83
using System;
class Test
{
static void Main( )
{
long longValue = [Link];
int intValue = (int) longValue;
[Link]("(int) {0} = {1}", longValue, intValue);
}
}
(int) 9223372036854775807 = -1
Pour éviter une telle situation, vous pouvez utiliser l'instruction checked pour lever une exception
lorsqu'une conversion échoue, comme suit :
using System;
class Test
{
static void Main( )
{
checked
{
long longValue = [Link];
int intValue = (int) longValue;
[Link]("(int) {0} = {1}", longValue, intValue);
}
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 29 / 83
4. Instructions et exceptions
4.1. Introduction aux instructions
Un programme se compose d'une suite d'instructions. Au moment de l'exécution, ces instructions sont
exécutées les unes après les autres, dans l'ordre dans lequel elles figurent dans le programme, de
gauche à droite et de haut en bas.
Chaque bloc définit une portée. Une variable déclarée dans un bloc est qualifiée de variable locale. La
portée d'une variable locale s'étend de sa déclaration jusqu'à l'accolade fermante qui délimite la fin de
son bloc. Le bon usage veut qu'une variable soit déclarée dans le bloc le plus profondément imbriqué,
ainsi sa visibilité restreinte augmente la lisibilité du code.
int i;
{
int i; // Erreur : i déjà déclaré dans le bloc parent
...
}
Vous pouvez toutefois déclarer des variables portant le même nom dans des blocs frères. Les blocs
frères sont délimités par le même bloc parent et sont imbriqués au même niveau.
En voici un exemple :
{
int i;
...
}
...
{
int i;
...
}
Les variables peuvent être déclarées n'importe où dans un bloc d'instructions, mais il est toutefois
recommandé d'initialiser une variable au moment de sa déclaration.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 30 / 83
structuré, ce qui peut être obtenu à l'aide de constructions ou d'instructions de haut niveau. Ces
instructions peuvent être classées en trois catégories.
if ( minute == 60 )
minute = 0 ;
else
minute++ ;
L'instruction if évalue une expression booléenne afin de déterminer la marche à suivre. Si l'expression
booléenne a la valeur true, la première instruction incorporée est exécutée. Si l'expression booléenne a
la valeur false et qu'il y a une clause else, le contrôle est passé à la seconde instruction incorporée.
if ( minute == 60 )
minute = 0 ;
else if ( minute == -1 )
minute-- ;
else
minute++ ;
Un bloc switch peut contenir des déclarations. La portée d'une variable locale ou d'une constante
déclarée dans un bloc switch s'étend depuis sa déclaration jusqu'à la fin du bloc switch, comme
l'illustre l'exemple reproduit sur la diapositive.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 31 / 83
1 - Si l'une des constantes spécifiées dans une étiquette case est égale à la valeur de l'expression
switch, la liste des instructions qui suit l'étiquette case correspondante est exécutée.
2 - Si aucune constante case n'est égale à la valeur de l'expression switch et si l'instruction switch
contient une étiquette default, la liste des instructions qui suit l'étiquette default est exécutée.
3 - Si aucune constante case n'est égale à la valeur de l'expression switch et si l'instruction switch ne
contient pas d'étiquette default, le contrôle est transféré à la fin de l'instruction switch.
switch (atout)
{
case [Link] :
couleur = "Noir";
break;
case [Link] :
couleur = "Rouge";
break;
default :
couleur = "ERREUR";
break;
}
L'instruction switch évalue uniquement les types d'expressions suivants : tous les entiers, char, enum
et string. Vous pouvez toutefois évaluer d'autres types d'expressions à l'aide de l'instruction switch du
moment qu'il existe précisément une conversion implicite définie par l'utilisateur du type non autorisé
vers les types autorisés.
Pour respecter cette règle, appelée règle de non-passage, vous pouvez recourir à l'une des instructions
suivantes : break (probablement la plus courante), goto (très rare), return, throw ou une boucle
infinie.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 32 / 83
3 - Si l'expression booléenne a la valeur false, le contrôle est transféré à la fin de l'instruction while.
Par conséquent, le programme réexécute l'instruction incorporée tant que l'expression booléenne a la
valeur true.
L'expression booléenne étant testée au début de la boucle while, il est possible que l'instruction
incorporée ne soit jamais exécutée.
int i = 0;
while (i < 10)
{
[Link](i);
i++;
}
// affiche : 0 1 2 3 4 5 6 7 8 9
4.3.2. L'instruction do
L'instruction do est toujours accompagnée d'une instruction while. Elle est similaire à l'instruction
while, à cette différence près que l'analyse de l'expression booléenne qui détermine la poursuite ou la
sortie de la boucle s'effectue en fin de boucle, et non pas au début. Cela signifie que, contrairement à
une instruction while qui est exécutée zéro ou plusieurs fois, une instruction do est exécutée une au
moins fois.
int i = 0;
do
{
[Link](i);
i++;
}
while (i < 10)
// affiche : 0 1 2 3 4 5 6 7 8 9
La syntaxe de l'instruction for est pratiquement identique à celle de l'instruction while, comme
l'illustre l'exemple suivant :
initialiseur
while ( condition )
{
instruction-incorporée
actualisation
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 33 / 83
Comme dans toutes les instructions de répétition, la condition d'un bloc for doit être une expression
booléenne qui tient lieu de condition de poursuite de la boucle et non pas de condition de fin.
Exemple
Comme pour les instructions while et do, vous pouvez utiliser une seule instruction incorporée ou un
bloc d'instructions, comme dans les exemples suivants :
// affiche : 0 1 2 3 4 5 6 7 8 9
// affiche : 0 -9 1 -8 2 -7 3 -6 4 -5 5 -4 6 -3 7 -2 8 -1 9 0
Déclaration de variables
Il existe une petite différence entre les instructions while et for : la portée d'une variable déclarée dans
le code d'initialisation d'une instruction for est limitée au bloc for. Ainsi, l'exemple suivant génère une
erreur de compilation :
Notez, par ailleurs, que vous ne pouvez pas déclarer dans un bloc for une variable portant le même
nom qu'une autre variable placée dans un bloc externe. Ainsi, l'exemple suivant génère une erreur de
compilation :
int i;
for (int i = 0; i < 10; i++) // Erreur : i est déjà dans la portée
En outre, vous pouvez initialiser deux variables ou plus dans le code d'initialisation d'une instruction
for de la manière suivante :
Les variables doivent toutefois être du même type. Par conséquent, l'exemple suivant n'est pas autorisé :
Pour mettre plusieurs instructions dans le code d'actualisation d'une instruction for, il faut les séparer
par une virgule ou plusieurs virgules, comme suit :
L'instruction for est adaptée aux situations dans lesquelles le nombre d'itérations est connu. Elle est
également particulièrement appropriée pour modifier chaque élément d'un tableau.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 34 / 83
Microsoft .NET Framework offre une classe de collection simple nommée ArrayList. Elle permet de
créer une variable de collection et d'ajouter des éléments à la collection. Considérons une collection
comme une liste.
C# fournit l'instruction foreach, qui permet de parcourir chaque élément d'une collection sans recourir
à une longue liste d'instructions. Plutôt que d'extraire explicitement chaque élément d'une collection
avec une syntaxe spécifique à la collection, l'instruction foreach permet d'aborder le problème
différemment. Vous demandez en fait à la collection de présenter ses éléments, les uns à la suite des
autres. Ce n'est plus l'instruction incorporée qui est apportée à la collection, c'est la collection qui est
apportée à l'instruction incorporée.
using [Link];
L'instruction goto peut transférer le contrôle à l'extérieur d'un bloc, mais ne peut jamais le transférer
dans un bloc. Le but de cette restriction est d'éviter de sauter une initialisation. Cette même règle est
appliquée en C++ et dans d'autres langages
.
L'instruction goto et l'étiquette cible peuvent être très éloignées l'une de l'autre dans le code. Cette
distance pouvant obscurcir la logique du flux de contrôle, la plupart des guides de programmation
recommandent de ne pas utiliser les instructions goto.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 35 / 83
Even:
[Link]("even");
End:;
// affiche : even
Les instructions goto sont recommandées dans les deux cas suivants : dans les instructions switch et
pour sortir d'une boucle imbriquée.
Les instructions break et continue ne sont pas très différentes d'une instruction goto, qui elle peut
aisément obscurcir la logique du flux de contrôle.
int i = 0;
while (true)
{
[Link](i);
i++;
if (i < 10)
continue;
else
break;
}
// affiche : 0 1 2 3 4 5 6 7 8 9
Ce type de code d'erreur ne permet pas de fournir aisément les informations qui aideront à la réparer.
Ainsi, la génération de IOError ne vous renseigne pas sur le type précis de l'erreur.
S'agit-il d'une tentative d'écriture sur un fichier en lecture seule ou sur un fichier inexistant, ou d'un
disque corrompu ? Sur quel fichier a lieu la tentative de lecture ou d'écriture ?
Pour remédier à ce manque d'information sur l'erreur générée, .NET Framework a défini un ensemble
de classes d'exceptions.
Toutes les exceptions sont héritées de la classe nommée Exception, qui fait partie du Common
Language Runtime. L’image ci-dessus présente la hiérarchie de ces exceptions.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 36 / 83
Le type de classe doit être soit [Link], soit un type dérivé de [Link].
L'identificateur, qui est facultatif, est une variable locale en lecture seule dans la portée du bloc catch.
try {
[Link]("Entrez un nombre");
int i = [Link]([Link]());
}
catch (OverflowException caught)
{
[Link](caught);
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 37 / 83
L'exemple de code ci-dessus illustre l'utilisation des instructions try et catch. Le bloc try entoure une
expression susceptible de générer une exception. Si l'exception est levée, le Runtime interrompt
l'exécution et cherche un bloc catch pouvant intercepter cette exception (en fonction de son type). Si le
Runtime ne trouve pas de bloc catch approprié dans la fonction la plus proche, il déroule la pile des
appels pour trouver l'appel de fonction. Si le bloc catch approprié ne s'y trouve pas, il recherche la
fonction qui a émis l'appel, et ainsi de suite jusqu'à ce qu'il trouve un bloc catch approprié (Ou jusqu'à
ce qu'il atteigne la fin de Main, auquel cas le programme s'arrête). Si le Runtime trouve un bloc catch,
il considère que l'exception est interceptée, et il reprend l'exécution du code à partir du corps du bloc
catch.
Une exception est interceptée uniquement sur la base de son type. Le runtime intercepte
automatiquement les objets exception d'un type particulier dans un bloc catch du type correspondant.
Considérons le code suivant pour mieux appréhender le fonctionnement d'un bloc try-catch multiple :
1. try {
2. [Link]("Entrez le premier nombre");
3. int i = [Link]([Link]());
4. [Link]("Entrez le second nombre");
5. int j = [Link]([Link]());
6. int k = i / j;
7. }
8. catch (OverflowException caught)
9. {
10. [Link](caught);
11. }
12. catch(DivideByZeroException caught)
13. {
14. [Link](caught);
15. }
La ligne 3 initialise int i avec une valeur lue à partir de la console. Cette opération est susceptible de
lever un objet exception de la classe OverflowException. Si c'est le cas, les lignes 4, 5 et 6 ne sont pas
exécutées. L'exécution séquentielle normale est interrompue et le contrôle d'exécution est transféré sur
le premier bloc catch capable d'intercepter cette exception. Dans cet exemple, il s'agit du bloc catch de
la ligne 8.
Si aucune des instructions du bloc try ne lève d'exception, le flux de contrôle atteint la fin de ce bloc.
Remarquez que le flux de contrôle n'entre dans un bloc catch que si une exception a été levée.
Vous pouvez écrire les instructions dans un bloc try sans vous inquiéter de savoir si une instruction
antérieure dans ce bloc risque d'échouer. Si une instruction antérieure lève une exception, le flux de
contrôle n'atteindra pas physiquement les instructions qui la suivent dans le bloc try.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 38 / 83
Dans cet exemple, l'instruction throw lève une exception définie par l'utilisateur,
InvalidTimeException, si le temps analysé ne correspond pas à une valeur de temps correcte.
En règle générale, les exceptions sont créées avec une chaîne de message significatif en paramètre. Ce
message est affiché ou enregistré au moment où l'exception est détectée. Il est également recommandé
de lever une classe d'exceptions appropriée.
Le bloc finally est utile dans deux circonstances : pour éviter la duplication d'instructions et pour
libérer des ressources après la levée d'une exception.
try {
...
}
catch {
...
}
finally {
instructions
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 39 / 83
5. Méthodes et paramètres
5.1. Utilisation des méthodes
5.1.1. Définition des méthodes
Une méthode est un groupe d'instructions C# qui ont été rassemblées et auxquelles un nom a été
attribué. La plupart des langages de programmation modernes partagent un concept similaire ; vous
pouvez vous représenter une méthode comme une fonction, une sous-routine, une procédure ou un
sous-programme.
Exemples de méthodes
L'exemple suivant montre comment créer une méthode appelée ExampleMethod dans la classe
ExampleClass :
using System;
class ExampleClass
{
static void ExampleMethod( )
{
[Link]("Méthode ExampleMethod");
}
La méthode Main est le point d'entrée de l'application. La méthode WriteLine fait partie intégrante de
Microsoft® .NET Framework. C’est une méthode statique de la classe [Link]. La méthode
ExampleMethod appartient à la classe ExampleClass.
Dans le langage C#, toutes les méthodes appartiennent à une classe. Ce n'est pas le cas des langages de
programmation tels que C, C++ et Microsoft Visual Basic®, qui autorisent l'utilisation de fonctions et
de sous-routines globales.
Création de méthodes
Lorsque vous créez une méthode, vous devez spécifier les éléments suivants :
- Nom :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 40 / 83
Vous ne pouvez pas attribuer à une méthode un nom identique à celui d'une variable, d'une constante
ou de tout élément qui n'est pas une méthode déclarée dans la classe. Il est possible d'utiliser comme
nom de méthode tout identificateur C# autorisé. Ce nom respecte la casse.
- Corps de la méthode :
À la suite des parenthèses, se trouve le corps de la méthode. Vous devez placer le corps de la méthode
entre accolades ({ et }), même si une seule instruction est utilisée.
Pour appeler une méthode, utilisez le nom de la méthode suivi de la liste des paramètres entre
parenthèses. Les parenthèses sont requises même si la méthode que vous appelez ne contient aucun
paramètre, comme le montre l'exemple suivant.
MethodName( );
using System;
class ExampleClass
{
static void ExampleMethod( )
{
[Link]("Hello, world");
}
Par défaut, une méthode retourne à son appelant lorsque la fin de la dernière instruction du bloc de
code est atteinte. Si vous souhaitez qu'elle retourne immédiatement à l'appelant, utilisez l'instruction
return.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 41 / 83
Dans l'exemple suivant, la méthode affiche « Hello » et retourne immédiatement à son appelant :
Il n'est pas très utile d'utiliser l'instruction return de cette façon, car l'appel final de la méthode
[Link] n'est jamais exécuté.
L'exemple suivant montre comment déclarer une méthode nommée TwoPlusTwo qui retourne la
valeur 4 à la méthode Main lorsque la méthode TwoPlusTwo est appelée :
class ExampleReturningValue
{
static int TwoPlusTwo( )
{
int a,b;
a = 2;
b = 2;
return a + b;
}
}
Notez que la valeur retournée est un entier (int). int est en effet le type de retour de la méthode.
Lorsque la méthode est appelée, la valeur 4 est retournée.
Si vous déclarez une méthode avec un type non void, vous devez ajouter au moins une instruction
return. Le compilateur vérifie que chaque méthode non void retourne dans tous les cas une valeur à la
méthode appelante.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 42 / 83
L'exemple suivant montre comment déclarer une méthode avec des paramètres.
Cet exemple montre comment déclarer la méthode MethodWithParameters avec deux paramètres : n
et y. Le premier paramètre est du type int (entier) et le second du type string (chaîne). Notez que des
virgules séparent chaque paramètre dans la liste des paramètres.
Le code suivant illustre une façon d'appeler une méthode avec des paramètres. Les valeurs des
paramètres sont trouvées et placées dans les paramètres n et y au début de l'exécution de la méthode
MethodWithParameters.
À l'intérieur de la méthode, vous pouvez écrire du code qui modifie la valeur du paramètre. Ceci n'aura
aucun effet sur les variables situées en dehors de l'appel de la méthode.
Dans l'exemple suivant, la variable x à l'intérieur de la méthode AddOne et peut être modifiée dans la
méthode AddOne, mais cette modification n'a pas d'effet sur la variable k.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 43 / 83
L'exemple suivant montre ce qui se produit si un paramètre par référence k n'est pas initialisé avant
que sa méthode AddOne ne soit appelée :
int k;
AddOne(ref k);
[Link](x);
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 44 / 83
Les paramètres de sortie sont identiques aux paramètres par référence, sauf qu'ils transfèrent des
données à l'extérieur de la méthode et non pas à l'intérieur. Tout comme un paramètre par référence,
un paramètre de sortie est une référence à un emplacement de stockage fourni par l'appelant.
Toutefois, il n'est pas nécessaire d'assigner une valeur à la variable fournie pour le paramètre out avant
que l'appel ne soit effectué ; la méthode suppose que le paramètre n'a pas été initialisé à l'entrée.
Les paramètres de sortie sont utiles lorsque vous voulez retourner des valeurs à partir d'une méthode
au moyen d'un paramètre sans assigner de valeur initiale à ce dernier.
Tout comme c'est le cas avec le mot clé ref, le mot clé out assigne un seul paramètre, et chaque
paramètre out doit être marqué séparément.
Lorsque vous appelez une méthode avec un paramètre out, placez le mot clé out avant la variable à
passer, comme dans l'exemple suivant :
int n;
OutDemo(out n);
Dans le corps de la méthode appelée, aucune supposition de départ n'est faite sur le contenu du
paramètre de sortie. Il est traité comme une variable locale non assignée. Une valeur doit être assignée
au paramètre out à l'intérieur de la méthode.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 45 / 83
6. Tableaux
[Link] d'ensemble des tableaux
6.1.1. Qu'est ce qu'un tableau ?
Les tableaux sont des séquences de données de même type. Vous pouvez accéder à un élément
individuel d'un tableau en utilisant sa position d'entier, qui s'appelle un index.
Les tableaux autorisent l'accès aléatoire. Les éléments d'un tableau sont situés dans la mémoire
contiguë. Cela signifie qu'un programme peut accéder aussi rapidement à tous les éléments d'un
tableau.
En C#, la notation de tableau est identique à celle employée par C et C++, à ces différences près :
Pour déclarer un tableau à deux dimensions, vous utilisez une virgule à l'intérieur des crochets, comme
illustré sur la diapositive. Un tableau de ce type s'appelle également tableau de rang 2, car deux index
entiers sont associés à chaque élément du tableau. Cette notation s'étend simplement comme suit :
chaque virgule supplémentaire spécifiée entre les crochets incrémente d'une unité le rang du tableau.
Vous n'incluez pas la longueur des dimensions dans la déclaration d'une variable tableau.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 46 / 83
accéder à un élément à l'intérieur d'un tableau de rang n, utilisez n index entiers séparés par des
virgules.
Les index de tableau (de tout rang) démarrent à zéro. Pour accéder au premier élément à l'intérieur
d'une ligne, utilisez l'expression :
row[0]
row[1]
Pour vérifiez que les index entiers sont toujours à l'intérieur des limites, vérifiez manuellement les
limites d'index, à l'aide d'une condition de fin d'instruction for, comme suit :
La propriété Length est égale à la longueur totale du tableau, quel que soit le rang du tableau. Pour
déterminer la longueur d'une dimension spécifique, vous pouvez utiliser la méthode GetLength,
comme suit :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 47 / 83
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 48 / 83
7. Notions fondamentales de la
programmation orientée objet
C# est un langage orientée objet. Tout développeur C# (et tout développeur .NET) se doit de maîtriser
les termes et les principes de base de la programmation objet. Après avoir abordé les termes et les
concepts généraux de la programmation objet, nous verrons son implémentation avec C#.
[Link] et objets
La programmation orienté objet possède son propre vocabulaire et ses propres concepts que nous
devons définir et assimiler avant de présenter plus concrètement son implémentation dans C#.
Exemple :
Toutes les voitures ont un comportement commun (elles tournent, s'arrêtent, etc.) et des attributs
communs (quatre roues, un moteur, etc.). Vous employez le mot voiture pour faire référence à ces
propriétés et ces comportements communs. Imaginez que l'on ne puisse pas les classer en concepts
nommés : au lieu de dire voiture, il faudrait énumérer toutes les significations du mot voiture.
Exemple :
Une personne a un nom, une date de naissance, une adresse et peut marcher, courir, sauter etc....
Jean Martin, né le 23 décembre 1984 est une instance de la classe personne.
Une structure n’a pas d’identité et deux structures ayant le même état fonctionneront de la même
manière que vous utilisiez l’une ou l’autre. L’état d’une structure est directement accessible et ne
possède pas forcément de comportement.
Les structures peuvent effectuer des opérations, mais cela n'est pas recommandé. Il est préférable
qu'elles ne contiennent que des données.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 49 / 83
Comme nous l’avons vu précédemment, une classe possède une identité, un état inaccessible et un
comportement. Les types représentés par des classes sont appelés des types référence en C#. Par
rapport aux structures, rien à l'exception des méthodes ne doit être visible dans une classe bien conçue.
Types valeur et Types référence : Une valeur de type valeur est présente dans la pile de l’application
en tant que variable locale ou en tant qu’attribut de l’objet auquel elle appartient.
Une valeur de type référence ne contient qu’un pointeur vers ses données présentes dans le tas
managé.
[Link] de l’encapsulation
L’encapsulation est le mécanisme permettant de rassembler les attributs et méthodes propres à un type
donné afin d’en restreindre l’accès et/ou d’en faciliter l’utilisation et la maintenance.
L’encapsulation permet de masquer à l’utilisateur d’une classe son implémentation, c'est-à-dire les
données qu’elle possède et les opérations qu’elle effectue afin de ne dévoiler que ce qui lui est
réellement utile.
Exemple :
Lorsque vous effectuez un virement bancaire, un certain nombre de transactions et vérifications sont
effectuées par votre banque. Ces opérations ne vous sont pas dévoilées (imaginez que vous puissez
directement modifier le solde de votre compte…). Vous ne pouvez et n’avez pas à effectuer vous-
mêmes ces opérations. De plus, si ces opérations sont mises à jour ou qu’une nouvelle vérification est
effectuée, vous ne modifierez pas votre manière d’effectuer le virement.
De manière générale, les données sont privées et les méthodes sont publiques. Cependant certaines
méthodes peuvent être privées et ne servir qu’au fonctionnement interne de la classe.
On peut toutefois avoir besoin de définir une valeur commune pour l’attribut d’un type. L’utilisation
de données statiques permet de partager une même valeur d’un attribut d’une classe avec tous les
objets de cette classe.
Exemple :
Lors de la définition d’un compte épargne, le taux d’intérêt de ce type de compte sera commun à tous
les comptes ouverts.
Ces données statiques peuvent être amenée à être modifié. On utilise alors des méthodes statiques
appelées directement sur la classe. Les méthodes statiques ne peuvent agir que sur les données
statiques.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 50 / 83
Si l’accès à un attribut de classe n’est pas défini, le compilateur C# le considèrera par défaut comme
privé.
class Program
{
static void Main( )
{
BankAccount yours = new BankAccount( );
[Link](999999M);
}
}
Ainsi, dans la classe ci-dessous les deux lignes de code sont équivalentes.
class BankAccount
{
[Link] = name;
name = name;
}
Le mot clé this est très utilisé lorsque un conflit de nom apparaît.
class BankAccount
{
public void SetName(string name)
{
[Link] = name;
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 51 / 83
L’utilisation du mot clé this permet ici d’assigner la valeur du paramètre name passée à la méthode
SetName à l’attribut name de l’objet.
Attention : en cas de conflit de nom, aucun avertissement n’est indiqué par compilateur C# et l’attribut
name de l’objet ne sera pas modifié.
Le mot clé this, faisant appel à l’objet et non à la classe elle-même, vous ne pouvez pas l’utiliser afin
d’accéder aux données et aux méthodes statiques.
Une classe imbriquée se déclare au sein même de la classe et peut être publique ou privée.
class Bank
{
public class Account { .... }
private class AccountNumberGenerator { .... }
}
7.8.1. Héritage
L’héritage est le mécanisme au moyen duquel les éléments les plus spécifiques incorporent la structure
et le comportement d’éléments plus généraux.
Une classe qui hérite d’un type devient en quelque sorte, une précision ou une spécialisation de ce
type.
La classe la plus générale est appelé classe de base ou classe parente, la plus spécifique, la classe
dérivée.
Exemple :
Un violoniste est un type, un musicien en est un autre. Cependant, un violoniste est également et avant
tout un musicien. On peut donc faire hériter le type violoniste des caractéristiques de tout musicien.
Toute modification apportée au type musicien sera ainsi directement reportée au type violoniste.
Lorsque l’on remonte dans la hiérarchie, nous parlons de généralisation, à l’inverse, la descente niveau
par niveau de la hiérarchie des classes représente une spécialisation.
Il est essentiel de limiter le nombre de niveaux d’une hiérarchie de classes afin d’en garder le contrôle.
Une hiérarchie des classes doit normalement se limiter à 6 ou 7 niveaux.
Exemple :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 52 / 83
Une voiture est un véhicule et peut donc hériter des caractéristiques communes à tout véhicule (rouler,
tracter etc.).
Une voiture peut également être un objet de collection et hériter caractéristiques communes des objets
de collections (côte, nombre d’exemplaires etc.).
De ce point de vue, il vous incombe de choisir ce que vous souhaitez faire de votre voiture. Si vous
souhaitez voyager avec votre voiture, vous l’utiliserez comme un objet de type véhicule. Si vous
voulez la revendre, vous l’utiliserez peut être comme un objet de collection.
L’héritage multiple vous amène à faire un très mauvais usage de celui-ci. C’est pourquoi C# n’autorise
pas ce procédé. Vous pouvez n’hériter que d’un seul type. Pour palier à l’absence d’héritage multiple,
nous verrons dans la présentation des interfaces que nous pourrons implémenter plusieurs interfaces
dans une même classe.
7.8.4. Polymorphisme
Le polymorphisme est le mécanisme permettant à un objet de prendre la forme de plusieurs types en
fonction des types de bases desquels ils héritent ou des interfaces qu’il implémente.
L’exemple ci-dessus illustrant l’héritage multiple peut être repris pour illustrer le polymorphisme. En
effet, une voiture peut à un instant t est utilisé comme
Les classes abstraites contiennent très souvent des méthodes statiques afin de pouvoir agir sur des
objets de type dérivé de la classe abstraite.
7.8.6. Interfaces
Une interface représente un contrat que la classe qui l’implémente se doit de remplir.
Une interface ne contient que des opérations (méthodes non implémentées) et ne peut donc pas être
instanciée.
Exemple :
Un ordinateur et un téléviseur sont deux types qui peuvent être allumés. Cependant, il est difficilement
concevable de faire hériter l’un de l’autre. C’est pourquoi, il conviendra ici de déclarer une interface
contenant l’opération allumer et de l’implémenter dans les classes ordinateur et téléviseur.
La liaison tardive (ou liaison dynamique) se présente lorsque que l’on effectue l’appel de méthode non
plus sur l’objet lui-même mais via une opération de classe de base.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 53 / 83
Les données de type référence contiennent une référence aux données stockées dans une zone séparée
de la mémoire (tas managé).
On peut comparer les variables de type référence aux pointeurs en C et C++ sans ne jamais omettre
qu’en C#, une variable de type référence ne peut jamais désigner un objet invalide. De plus, aucune
gestion de la mémoire n’est nécessaire puisque cela est strictement réservé au Garbage Collector.
Prenons pour exemple la classe Coordinate représentant les coordonnées d’un point.
class coordinate
{
public int x;
public int y;
}
coordinate c1;
Cependant, je n’ai ici fait que déclarer la référence vers un Coordinate et il m’est donc impossible
d’assigner des valeurs à mon objet.
Pour cela, je dois désormais utiliser l’opérateur new afin de créer les données que référencera c1 :
c1 = new coordinate();
Lors de l’instanciation, un appel vers le constructeur de l’objet est effectué. Cependant, ma classe
Coordinate n’implémente aucun constructeur. Le compilateur C# appelle donc un constructeur
implicite (constructeur par défaut) qui va initialiser les attributs de type simple (type valeur) à leur
valeur d’origine. Actuellement, nous aurons donc :
c1.x = 0;
c1.y = 0;
Cependant, si aucun constructeur n’est implémenté, les objets de type référence déclaré dans la classe
ne peuvent être instanciés.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 54 / 83
En C#, vous ne pouvez accéder à des références invalides. Lors de la compilation, une erreur empêche
la compilation si une référence n’est pas initialisée et une exception est levée lors d’une tentative
d’accès à une référence non valide pendant l’exécution.
Ainsi, si nous déclarons un objet de type [Link], qui permet de générer des nombres
aléatoires dans notre classe Coordinate, nous ne pourrons pas l’utiliser et à l’exécution, une exception
de type [Link] sera levée.
class Coordinate
{
public [Link] rnd;
public int x;
public int y;
}
…
[Link](); //exception levée!!!
…
c1.x = 6.12;
c1.y = 4.2;
c1 fait désormais référence à un objet de type Coordinate dont les attributs x et y ont été
respectivement assignées à 6,12 et 4,2.
c1.x = null;
class Program
{
static void Main(string[] args)
{
Coordinate c1 = new Coordinate();
Coordinate c2 = new Coordinate();
c1.x = 1.0;
c1.y = 2.0;
c2.x = 1.0;
c2.y = 2.0;
if(c1 == c2)
[Link]("Même chose!!!");
else
[Link]("Pas la même chose!!!");
[Link]();
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 55 / 83
En effet même si c1 et c2 ont les mêmes valeurs, ils font référence à deux objets différents.
Pour que deux variables de type référence soient égales, il faut qu’il fasse référence au même objet.
class Program
{
static void Main(string[] args)
{
Coordinate c1 = new Coordinate();
Coordinate c2;
if(c1 == c2)
[Link]("Même chose!!!");
else
[Link]("Pas la même chose!!!");
[Link]();
}
}
Même chose!!!
Les deux variables sont désormais équivalentes puisque faisant référence au même objet.
Nous avons vu auparavant que les structures étaient des variables de type valeur. Transformons donc
la classe Coordinate en une structure StructCoordinate.
struct StructCoordinate
{
public int x;
public int y;
}
Ecrivons maintenant une classe Program implémentant une méthode incrémentant les coordonnées.
Cette méthode sera surchargée afin de pouvoir accueillir une référence ou une valeur comme
paramètre.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 56 / 83
class Program
{
static void Main(string[] args)
{
Coordinate c1 = new Coordinate();
StructCoordinate c2 = new StructCoordinate();
[Link]([Link]("c1: {0};{1}",c1.x,c1.y));
[Link]([Link]("c2: {0};{1}",c2.x,c2.y));
[Link]();
}
Sortie console :
C1 : 1 ;1
C2 : 0 ;0
L’action de la méthode ChangeCoordinate est différente suivant le type de variable transmise comme
paramètre.
Lorsque vous fournissez des variables de type valeur, la méthode agît sur une copie de la structure de
données. Dans le cas présent, c’est une copie de la structure c2 qui est transmise à la méthode. Cette
copie est une variable locale et sera détruite à la fin de la méthode.
L’utilisation d’une référence comme paramètre fournit à la méthode l’emplacement des données dans
le tas managé. Ainsi, les modifications apportées aux données s’effectuent sur les données référencées
par c1 et sont donc conservées après l’exécution de la méthode.
On peut outrepasser le fait que le paramètre utilisé dans une méthode soit une copie de la variable
passée en paramètre à l’aide de l’utilisation du mot clé ref.
On peut modifier le code précédent pour passer la structure en ajoutant le mot clé ref.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 57 / 83
ChangeCoordinate(ref c1);
[Link]([Link]("c1 : {0};{1}",c1.x,c1.y));
[Link]();
}
Sortie console :
C1 : 1 ;1
Les modifications apportées ont cette fois-ci était conservées après l’exécution de la méthode.
Pour utiliser le mot clé ref, nous devons instancier l’objet avant de le passer en paramètre à la
méthode. Le mot clé out permet d’instancier l’objet passé en paramètre seulement au niveau de la
méthode appelée.
ChangeCoordinate(out c1);
[Link]([Link]("c1 : {0};{1}",c1.x,c1.y));
[Link]();
}
coor.x++;
coor.y++;
}
On obtient alors le même résultat qu’avec l’utilisation de ref. Seul l’endroit d’instanciation diffère.
8.5.1. [Link]
La classe [Link] dont nous avons déjà abordé l’utilisation est un type référence. Cette
classe représente une erreur générique dans une application. Les erreurs peuvent être déclenchées à
l’aide de l’opérateur throw.
De nombreuses classes dérivées de [Link] afin de mieux cibler l’origine de l’erreur :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 58 / 83
[Link]
[Link]
[Link]
…
Vous pouvez de plus créer vos propres exceptions en créant une classe héritant de [Link].
8.5.2. [Link]
[Link](new object().ToString());
Cependant, cette méthode peut être redéfinie selon l’usage qu’on fait de la classe.
Ainsi, pour un int il est plus efficace de retourner la valeur contenu dans la variable sous forme de
chaîne.
int i = 3;
[Link]([Link]());
…
Coordinate a = new Coordinate();
Coordinate b = new Coordinate();
a = b;
Cependant certains types redéfinissent cette méthode afin de comparer les valeurs contenues comme
c’est le cas pour la classe string.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 59 / 83
string a = "salut";
string b = "salut";
8.6.7. Réflexion
La réflexion vous permet de récupérer dynamiquement (à l’exécution), des informations sur les types
utilisés. Les classes utiles à la réflexion sont regroupées dans l’espace de noms [Link]. Ces
classes sont nombreuses et une étude exhaustive de la réflexion sort du cadre de ce cours.
Les noms globaux sont composés des noms des différents espaces de noms suivis du nom de type.
Le type [Link] par exemple est contenu dans le namespace
[Link].
Si vous souhaitez écrire votre propre classe MailMessage, vous pouvez inclure celle-ci dans votre
propre espace de noms à l’aide de l’instruction namespace{ }.
namespace [Link]
{
class Read
{
...
}
class Write
{
...
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 60 / 83
Tous les types en C# sont inclus dans un espace de noms, même si vous n’en précisez pas (par défaut
les types sont contenus dans un espace de noms portant le nom de l’application).
Les espaces de noms permettent donc de conserver des noms de type explicites en évitant les conflits
de noms tout en organisant le code.
Cependant, les noms globaux ne sont pas pratiques à manipuler et l’on préférerait s’en tenir au nom de
type dans le code.
La directive using permet de spécifier les différents espaces de noms avec lesquels on souhaite
travailler dans le fichier en cours.
using System;
using [Link];
using SQL = [Link];
namespace Methodes_Object
…
Dans l’exemple ci-dessus, un alias a été défini pour l’espace de noms [Link].
Classes FileStream
La classe FileStream peut être utilisée pour permettre l'accès aléatoire aux fichiers.
L’étude du support d’XML dans .NET fait l’objet d’un cours complet.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 61 / 83
[Link] de données
.NET est fortement typé et il est souvent nécessaire de convertir les données afin de remplir
d’effectuer certaines opérations.
Cette section traitera donc de la conversion des types valeurs et références ainsi que du mécanisme de
boxing/unboxing.
int a = 6;
long b = a; //conversion implicite de a en 'long'
long a = 6;
int b = a;
long a = 6;
int b = (int)a;
Cependant si a est incrémenté et que sa longueur dépasse la capacité d’un int (4 octets), la conversion
sera refusée et une exception de type System. OverflowException sera levée.
La conversion d’un type enfant vers un type parent peut être implicite, est sûre et toujours autorisé.
string a = "salut";
object o = a;
La conversion d’un type parent vers un type enfant doit être explicitement effectuée. Aucune
vérification n’est effectuée avant l’exécution de l’application. Lors de la conversion, les types sont
vérifiés. Si une perte de données peut être causées par l’opération, elle sera annulée et une exception
de type [Link] sera levée.
L’opérateur is permet de vérifier le type d’un objet et renvoie True si les deux opérandes sont de
mêmes types.
Bird b;
if(a is Bird)
{
b = (Bird)a;
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 62 / 83
L’opérateur as permet d’effectuer une opération de conversion sans générer d’erreur en cas de cast
invalide. Si la conversion n’est pas sûre, as renvoie null.
8.8.4. Boxing/Unboxing
C# inclut un système de types unifiés qui permet de convertir les types valeur en références de type
[Link], ainsi que de convertir des références d'objets en types valeur. Les types valeur peuvent
être convertis en références de type [Link], et vice-versa.
Le Boxing consiste donc à convertir une variable de type valeur en un objet de type référence
[Link].
int i = 123;
object box;
L’Unboxing consiste à convertir un type [Link] en un type valeur. Cette conversion ne peut
être qu’explicite.
i = (int)box;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 63 / 83
[Link] de constructeurs
En C#, la seule manière de créer un objet consiste à utiliser le mot clé new pour allouer la mémoire.
Il n’existe aucune exception à cette règle bien que le compilateur le fasse implicitement à votre place.
Les deux lignes ci-dessous sont par exemple identiques :
string a = "salut";
string b = new string(new char[]{'s','a','l','u','t'});
La deuxième étape de création des objets consiste à appeler un constructeur. L’appel du constructeur
crée l’objet dans l’espace mémoire alloué. On peut différencier deux types de constructeurs différents,
les constructeurs d’instances ou d’objets et les constructeurs statiques ou constructeurs de classes.
Un constructeur par défaut doit porter le même nom que la classe, être publique, ne prendre aucun
argument et ne renvoyait aucune valeur (pas même void).
Vous pouvez réécrire votre propre constructeur par défaut en suivant les règles présentées ci-dessus.
La réécriture peut trouver son utilité si vous n’êtes pas satisfait des valeurs par défaut que prennent les
membres de votre classe.
Dans l’exemple ci-dessous, le constructeur de la classe MyDate a était modifié afin que le membre
DateTime dt soit initialisé aux dates et heures actuelles plutôt qu’au 01/01/01.
class MyDate
{
public DateTime dt;
public MyDate()
{
dt = [Link];
}
}
Le constructeur peut être surchargé afin de passer des arguments différents et de pouvoir modifier
l’initialisation des membres selon le type de données lors de la construction de l’objet.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 64 / 83
class MyDate
{
public DateTime dt;
Attention : si vous surchargez le constructeur d’une classe, l’appel au constructeur par défaut implicite
est impossible. Il vous faudra alors écrire votre propre constructeur par défaut.
Une liste d'initialiseurs peut vous permettre d'écrire un constructeur appelant un autre constructeur
dans la même classe. Une liste d'initialiseurs commence par le signe deux-points, est suivie du mot clé
this, puis d'arguments entre parenthèses.
class MyDate
{
public DateTime dt;
public MyDate(DateTime dt) : this(2004,01,01)
{
[Link] = dt;
}
class Tailor
{
public static string helpMsg;
static Tailor()
{
def = "My tailor is rich!!!";
}
}
Vous ne pouvez appeler vous-mêmes un constructeur statique. Il est donc impossible de préciser
l’accès au constructeur de classe ou d’ajouter des arguments.
Les constructeurs statiques sont aussi appelés constructeurs de classe car ils n’ont accès qu’aux
membres statiques de classe. Ainsi, l’utilisation de this (référençant un objet) dans un constructeur
statique entraîne une erreur de compilation.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 65 / 83
struct MaStruct
{
public int x;
public int y;
Tous les membres de la structure doivent être explicitement initialisés dans le constructeur.
public MaStruct()
{
x = 0; //Erreur : Les structures ne peuvent pas contenir de
y = 1; // constructeurs exempts de paramètres explicites
}
[Link] et mémoire
En C# et en .NET, la gestion de la mémoire incombe entièrement et perpétuellement à la CLR. Nous
allons décrire cette gestion et le fonctionnement du garbage collector.
Nous avons vu comment initialiser des variables de type référence. Ce processus est composé des deux
étapes que sont la réservation de l’espace mémoire et l’initialisation des variables. La destruction de
l’objet est le processus exactement inverse, consistant à déinitialiser l’objet et à libérer la mémoire.
Cependant, le contrôle de ce processus ne nous vous est pas attribué. En effet, cette tâche est allouée
au garbage collector.
L’opération de destruction et son mauvais usage étant d’en d’autres langages une source importante
d’erreurs grave, .NET automatise la tâche.
Le garbage collector a pour but de :
¾ Déterminer les objets inaccessibles (non référencés).
¾ Libération de la mémoire.
Cette tâche s’accomplit lorsque la mémoire devient insuffisante. Vous ne pouvez déterminer le
moment de cette libération et donc de la destruction de vos objets.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 66 / 83
9.3.1. Finalize
Les objets .NET héritent de [Link] de la méthode Finalize. Finalize est exécutée lors de la
libération de l’objet mais ne peut être ni appelée ni substituée.
9.3.2. Destructeurs
Un destructeur est à l’inverse d’un constructeur destiné à déinitialiser les membres d’un objet.
Un destructeur est une sorte de méthode de la classe, portant le même nom que celle-ci, ne possédant
pas de modificateur d’accès, de type de retour (pas même void), de paramètres. De plus, un
constructeur est précédé par ~.
class SourceFile()
{
~SourceFile()
{
...
}
}
Vous devez limiter l’utilisation des destructeurs car vous ne pouvez contrôler leur moment
d’exécution. Cela pourrait entraîner des ralentissements dans le processus de libération de la mémoire
et ainsi générer des pertes de performances.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 67 / 83
Une classe dérivée ne peut pas être plus accessible que sa classe de base. Par exemple, il n'est pas
possible de dériver une classe publique d'une classe privée. La syntaxe C# pour dériver une classe
d'une autre classe est également autorisée en C++ où elle spécifie implicitement une relation d'héritage
privé entre les classes dérivées et les classes de base. C# n'autorise pas l'héritage privé ; tous les
héritages sont publics.
class Vehicule
{
protected string immatriculation;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 68 / 83
class Vehicule
{
public virtual void Deplacer()
{
...
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 69 / 83
La définition d’une interface est similaire à la définition d’une classe sans implémentation. On utilise
donc le mot clé interface et les méthodes déclarées dans l’interface ne doivent pas contenir de code.
interface IDeplacable
{
void Deplacer();
}
Une interface ne comprend que des méthodes. Les membres sont interdits dans une interface et les
méthodes de l’interface ne peuvent posséder de modificateurs d’accès.
}
}
Si la classe Voiture n’implémentait pas la méthode Deplacer, une erreur serait survenue à la
compilation.
Une utilisation possible des interfaces consiste à itérer les éléments d’une collection, dont on ignore le
type exact.
foreach(IDeplacable i in mesVehicules)
{
[Link]();
}
Comme mes véhicules implémentent l’interface IDeplacable, je suis certain que chaque véhicule
implémente la méthode Deplacer.
Exemple :
// [Link]
using System;
interface IDimensions
{
float Length();
float Width();
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 70 / 83
lengthInches = length;
widthInches = width;
}
float [Link]()
{
return lengthInches;
}
float [Link]()
{
return widthInches;
}
[Link]();
}
}
Sortie console :
Length: 30
Width: 20
Explications :
• Nous déclarons tout d’abord l’interface en précisant quels seront les membres et méthodes à
implémenter. En effet, utiliser les interfaces permet de forcer le développeur à développer un
certain nombre de méthode. Dans notre exemple, nous déclarons deux méthodes (Length() et
Width()). Notre classe Box implémentant cette interface, le développeur doit alors développer
ces méthodes. Box implémente ces interfaces en mettant en nom de méthode
[Link]() par exemple, ce qui indique que c’est l’implémentation de Length de
l’interface IDimensions.
• On peux alors appeler la méthode d’une interface depuis un objet, et ce quel que soit son type,
tant que l’objet implémente l’interface.
• Si besoin, le développeur peut implémenter autant d’interface qu’il le souhaite
• Commencez obligatoirement le nom de votre interface par un ‘I’.
• Le Framework .NET comprend un grand nombre d’interface que vous pouvez utiliser. Par
exemple, l’interface ICollection vous permet de créer des collections personnalisées.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 71 / 83
Au contraire des classes abstraites, certaines classes peuvent être scellées. Les classes scellées ne
peuvent être dérivées. Pour déclarer une classe scellée, on utilise le mot clef sealed.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 72 / 83
// [Link]
using System;
[Link]();
}
}
Explications :
• Nous avons tout d’abord créé une classe Complex contenant deux membres publics.
• Nous ne pouvons additionner deux objets directement. Il faut donc surcharger l’opérateur +.
Remarquez que la méthode est statique.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 73 / 83
• Cette méthode retourne un Complex et prends deux arguments de type Complex, mais nous
pouvons très bien créer autant de méthode que nécessaire pour les autres types (avec des int en
paramètres par exemple).
• Les opérateurs du c# sont : +, -, !, ~, ++, --, true, false, *, /, %, &, |, ^, <<, >>, ==, !=, <, >,
<=, >=.
11.2. Délégations
Les délégations sont un type un peu particulier. Elles permettent de fournir une fonction en paramètre
pour déléguer l’exécution d’un morceau de programme. C’est l’équivalent des pointeurs de fonctions
en C/C++.
Exécutez ce code :
// [Link]
using System;
Explications :
• On déclare tout d’abord un delegate via le mot clef homonyme. On lui précise les arguments
que la fonction déléguée devra accepter (ici un int).
• On crée ensuite un objet de ce type en lui précisant quelle est la fonction appelée. On peut
alors appeler la fonction désignée par l’intermédiaire de l’objet délégué.
11.3. Evénements
Les événements permettent de capturer une action du programme. Ainsi, lorsque l’on clique sur un
bouton, l’événement Click du bouton est levé. On peut capturer les événements émis par les classes du
Framework mais aussi créer ses propres événements.
Observons ce code :
// [Link]
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 74 / 83
[Link]("hello");
[Link]();
[Link]("Titi");
[Link]();
[Link]("Koala");
[Link]();
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 75 / 83
Résultat :
Starting Hooking
Changing _test.Msg to hello
I hook the Changed event from _test
Stoping Hooking
Changing _test.Msg to Titi
Starting Hooking
Changing _test.Msg to Koala
I hook the Changed event from _test
Explications :
• Un événement se déclare en plusieurs étapes. La première est de déclarer l’événement dans la
classe en l’associant à un gestionnaire d’événement (EventHandler). Ce gestionnaire est de
type delegate auquel nous pourrons associer une méthode qui sera donc le traitement de notre
événement.
• Ainsi dans notre exemple, dans la classe TestHooking, l’événement [Link] est capturé
via la ligne : [Link] += new EventHandler(Test_OnChanged);
• En réalité, comme EventHandler est un type par délégation, la classe Test délègue une
méthode. C’est pourquoi, pour lever l’événement Changed de la classe Test, on teste d’abord
si Changed est null (donc la délégation n’est pas effectuée) avec d’appeler la fonction
déléguée.
// [Link]
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 76 / 83
[Link]();
}
Sortie écran :
I built a R5
The factory has finished building a R5
I built a F350
The factory has finished building a F350
I built a Laguna
The factory has finished building a Laguna
Explications :
• Le principe est le même que l’exemple précédent, mais nous avons développé notre propre
type d’événement.
• Nous avons pour cela créé un type par délégation pour capturer notre événement personnalisé.
Chaque événement possède deux arguments : sender (object) qui est la source de l’événement,
et e (EventArgs) qui sont les arguments de l’événement.
• Nous avons alors justement hérité la classe EventArgs pour créer nos propres arguments
d’événement en rajoutant ici une chaîne de caractères Model.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 77 / 83
// [Link]
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 78 / 83
[Link]([Link]);
[Link]=4;
[Link]([Link]);
[Link]=6;
[Link]([Link]);
[Link]=-12;
[Link]([Link]);
[Link]=806;
[Link]=32;
[Link]=1;
[Link]([Link]);
[Link]([Link]());
[Link]();
}
}
Sortie écran :
3
4
6
-12
1
My value is 1, i've been accessed 5 times and updated 7 times.
Explications :
• Faire [Link]([Link])revient à appeler la méthode get de la propriété Value.
Dans cette propriété on a simplement retourné une valeur (qui doit d’ailleurs être du même
type que la propriété).
• A l’opposé, faire [Link]=32 reviens à appeler la méthode set de la propriété Value en lui
donnant comme argument 32 (que l’on récupère alors via le mot clef value).
Modifions la classe Personne vue lors à l’exemple sur les classes. Pour cela modifiez le code pour
obtenir :
// [Link]
using System;
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 79 / 83
{
_age=value;
EvaluateThinking();
}
}
// Le nom
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name=value;
EvaluateThinking();
}
}
// Membre privé
private string _thinkings;
// Constructeurs
public Personne(int Age,string Name)
{
[Link]=Age;
[Link]=Name;
}
public Personne():this(0,"Unknown")
{
}
// méthode privée
private void EvaluateThinking()
{
this._thinkings=[Link](
"My name is {0} and I'm {1} old",
Name,Age);
}
// méthode publique
public void SayMyThinkings()
{
[Link](_thinkings);
}
}
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 80 / 83
[Link]();
}
}
Explications :
• Comme je l'expliquais plus haut, on maîtrise l'affection ou la récupération des propriétés. Dans
cette exemple, le but est de gérer deux membres privés de la classe Personne _age et _name.
La différence se situe lors de l'appel à la fonction EvaluateThinkings. Elle ne se fait plus
manuellement dès que l'on a besoin de _thinkings, mais automatiquement dès que l'on met à
jour le nom ou l'age.
• Comment se déroule le programme ? La ligne [Link]="Robert" fait appel à la
fonction set de la propriété Name. Cette dernière a alors deux instructions, d'une part, l'appel à
la fonction EvaluateThinkings qui est nécessaire pour la mise à jour de _thinkings, et d'autre
part l'affectation de _name. Remarquez le mot clef value qui indique la valeur appelée par le
programmeur. Je vous conseille d'exécuter ce programme pas à pas pour bien visualiser l'ordre
d'exécution.
12.2. Indexeurs
Les indexeurs sont comparables aux propriétés. Cependant un indexeur permet d’accéder, non pas à
une seule variable comme à l’aide des propriétés mais à une collection de variables. Ainsi, la
collection définie par la classe pourra être consultée ou modifiée à l’aide de l’opérateur [].
class Application
{
static void Main(string[] args)
{
Eleve martin = new Eleve("Martin",14);
Eleve marc = new Eleve("Marc",13);
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 81 / 83
}
}
public Classe ()
{
_eleves = new ArrayList();
}
Sortie écran :
Martin 14
Marc 13
Explications :
• Une classe représentant une classe d’élèves est définie. Cette classe contient donc une
collection (ArrayList) d’élèves dont le type a été définie.
• L’indexeur permet d’accéder directement aux élèves de la classe.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 82 / 83
13. Attributs
Un attribut permet de spécifier des paramètres supplémentaires à une méthode, une classe, une
propriété. Ces paramètres peuvent servir à donner encore plus de détails dans les spécificités d’un
objet, d’une classe, etc. que les simples public, private, etc. On peut par exemple, spécifier qu’une
classe est visible depuis un WebService avec l’attribut [WebMethod], ou encore indiquer qu’une
méthode s’exécute dans un même espace mémoire en cas d’utilisations de threads avec [StaThread].
Remarquez que les attributs se déclarent en précédent la méthode ou la classe rattaché par un
[nomattribut1,nomattribut2,etc].
On peut également spécifier des paramètres à un attribut. En effet, chaque attribut est hérité de la
classe [Link] et on peut donc créer des constructeurs spécialisés.
// [Link]
using System;
using [Link];
[Flags]
public enum StateFlags
{
WithWheels= 0x0001,
WithEngine= 0x0002,
WithLights= 0x0004,
WithBreaks= 0x0008
}
State = [Link];
State |= [Link];
[Link]();
}
}
Sortie écran :
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs
Programmation en C# 83 / 83
Explications :
• Ici nous avons via l’attribut [Flags] (dont l’applicabilité est sur les énumérations
exclusivement) indiqué au compilateur qu’il doit traiter notre énumération comme un champ
de bit, et non une valeur entière.
• Le fonctionnement de la fonction diffère sans cet attribut. Essayez de supprimer l’attribut, et
vous verrez. Même si le programme fonctionne, il sera incapable d’afficher correctement
State. Si le flag est mis, il reconnaîtra la combinaison des deux valeurs.
• On voit donc comment avec les attributs on peut influencer le déroulement du programme.
[Link]
Ce document est la propriété de SUPINFO et est soumis aux règles de droits d’auteurs