Tutorial c#

Contenu du snippet

Si vous souhaitez apprendre le C# à partir de zéro et avez déjà des bases en programmation, ce tutorial loin d'être achevé vous sera peut-être utile ;)

Source / Exemple :


Qu'est-ce que .NET ou dotNET ?

dotNET et .NET signifient exactement la même chose. En anglais le "." se dit "dot" c'est la raison pour laquelle les deux expressions sont correctes. Je suis sûr que vous avez beaucoup entendu parler de .NET ces derniers temps (à condition que vous ne soyez pas trop anti-microsoft au point de n'être au courant de rien qui provient de chez eux) et il est probable que vous ne sachiez pas exactement en quoi cela consiste. C'est pourquoi je vais vous en donner ici une brève description : .NET constitue un ensemble de bibliothèques (librairies), la CLR (Common Language Runtime) qui s'occupe d'exécuter le code MSIL (voir plus loin), ainsi que de l'environnement ASP.NET. Tout ceci constitue le framework .NET. Ces bibliothèques sont à disposition des programmeurs de plus d'une trentaine de langages, parmi lesquels se trouvent le C#, VB.NET, VC.NET etc. Tout ces langages ont alors à disposition cette énorme base de données qu'est le framework. Le but de ce dernier étant de vous permettre, quel que soit votre langage (pour autant qu'il fasse partie de la trentaine disponibles), d'utiliser les mêmes objets ! dotNET fédère tous les langages de telle sorte que le programmeur peut utiliser son langage préféré ou le langage le plus adapté à la problématique en exploitant toujours le même environnement et en bénéficiant de performances à l'exécution de même niveau quel que soit le langage choisi. 
Cela parraît difficile à croire mais une fois le procédé assimilé, vous n'aurez plus de problème : toutes les sources codées pour un compilateur .NET sont compilée d'abord en un langage intermédiaire, IL (Intermediate Language). Pour une source C# ou VB.NET (ou tout langage .NET) vous aurez le même résultat en code IL, ou du moins un code très proche dépendant de l'optimisation qu'à apporté votre compilateur au code original ! C'est lors de la première exécution de votre programme que le compilateur JIT (Just In Time) va compiler votre application en version finale. Qu'est-ce que ça implique? 
- Une rapidité d'exécution équivalente quel que soit le langage .NET
- L'utilisation des même objets quel que soit le langage .NET

A côté des avantages de dotNET il y a tout de même quelques inconvénients :
- L'utilisateur doit posséder le framework pour pouvoir exécuter vos programmes. Ce dernier fait environ 21mo (plus le SP) ce qui le rend peu accessible pour l'instant. Il sera toutefois inclu dans les prochaines versions de windows.
- Des changements de syntaxe pour certains langages qui sont très conséquents. VB.NET par exemple n'a gardé du Visual Basic, pourrait-on dire, que le nom ;) Bon j'exagère un peu mais avec l'ajout de la programmation objet, etc. le langage a gardé son style mais devient beaucoup plus complexe pour un programmeur débutant. En effet microsoft en avait sûrement marre que le VB soit considéré comme un langage sans envergure et sans avenir commercial.
- Pour développer avec VisualStudio.NET vous aurez besoin d'une machine de guerre... 256mo de RAM sont tout juste suffisant, et un pentium III ou Athlon sont conseillés. En plus de cela, oubliez vite .NET si vous avez le vieux win98... Vous devez au minimum posséder win2000 professional ou winXP qui va très bien (winXP professionnal est requis pour utiliser l'ASP .NET car IIS ne fonctionne pas sur home edition).

Quel langage choisir ?

Comme je vous le disais, au niveau des performances, cela est absolument égal. Il vous faut donc principalement choisir en fonction de l'esthétique... Pour ceux qui programmaient en VB le VB.NET est évidemment le meilleur choix. Pour les programmeurs C++ choisissez le C# qui est très proche du C++ au niveau de l'écriture. Ce dernier est en effet une sorte de mixture du C++ et du Java ; il possède les qualités de chacun et personnellement je le trouve vraiment génial. Ce sont des langages orientés objets, donc avec héritage, polymorphisme etc. Pour ce qui est du C++, c'est un peu spécial. On peut l'utiliser en deux modes différents. Soit en utilisant le mode managé (Managed) soit le mode non-managé (Unmanaged). Si vous travaillez en mode non-managé, c'est l'équivalent de VC6 (avec des amélioration bien entendu) : le code est directement compilé en natif, binaire lisible par le processeur. Vous pouvez aussi travailler avec les bibliothèques MFC et compagnie et vous n'avez donc pas besoin du framework .NET sur la machine cliente (ce n'est donc pas un langage .NET). Si par contre vous programmez en managé, vous bénéficiez de tous les avantages du framework .NET, comme dans n'importe quel autre langage .NET. Il n'y a par contre pour l'instant pas d'éditeur visuel de fenêtres et vous allez devoir écrire tout le code vous-même. Celà sera résolu dans la prochaine version de Visual Studio (nom de code everret) prévue pour tout prochainement qui intégrera un RAD pour les applications managed C++.
Je n'ai parlé jusqu'ici que du C#, VB.NET et C++.NET mais plusieurs dizaines de langages .NET sont prévus au final. Je vous laisse le soin de vous renseigner au sujet d'autres langages qui ont vu ou vont voir le jour (Delphi.NET, Eiffel.NET, J#...).

Où trouver la plateforme de développement ?

Vous pouvez trouver le framework .NET à l'adresse suivante :
http://msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/msdn-files/027/001/829/msdncompositedoc.xml
Ainsi que le Service Pack 2 ici :
http://msdn.microsoft.com/netframework/downloads/updates/sp/download.asp

Compiler en fenêtre de commandes

Si vous n'avez pas la chance d'avoir Visual Studio .NET, deux solutions s'offrent à vous pour compiler vos applications. La première est de compiler à partir d'une fenêtre de commandes (pas génial effectivement), la deuxième est d'utiliser un autre environement de développement que VS.NET. Il en existe un particulièrement apprécié, encore en phase de beta mais tout à fait fonctionnel : SharpDevelop (pour autant que vous fassiez du C#) et vous pouvez le trouver à l'adresse suivante :
http://www.icsharpcode.net/OpenSource/SD/default.asp
Voyons maintenant pour la compilation en console. Pour compiler vos applications C#, vous avez un exécutable appelé csc.exe dans le répertoire du framework (en général au path suivant : C:\WINDOWS\Microsoft.NET\Framework\v1.0.****). Ouvrez une fenêtre de console (Exécuter => "cmd").
Pour compiler votre source tappez la commande : "csc NomSource.cs"
Suivie des arguments nécessaires dont vous obtenez la liste en mettant : "csc -help"
Voilà, à vous de jouer ;)

Commentaire

Voilà j'espère que jusque là vous suivez. Si ce n'est pas le cas essayez au moins de comprendre le concept de la programmation orientée objet en allant lire d'autres tutoriaux sur le sujet.
J'ai choisi le C# de la plate-forme .NET pour les exemples de cet ouvrage car c'est à mon goût le langage le plus lisible. Je vous vois venir avec vos "Non c'est le Visual Basic !", mais je ne suis pas du tout de cet avis. En effet lire un programme qui ne contient que des mots me parraît en règle générale très pénible (imaginez lire une source VB sans coloration et indentation : la mort de l'âme). Avec la syntaxe du C# qui provient en grande partie du C++ il y a des accolades, points-virgules etc. qui permettent d'organiser votre code de la manière qui vous plait le mieux, et donc de le rendre aussi lisible que possible. Enfin a vous de voir, je respecte évidemment votre choix, et de toute façon, transformer une source du C# vers du VB.NET ce n'est pas si ardu une fois que l'on connaît un peu les deux langages, et il existe d'ailleurs des programmes capables de le faire automatiquement. 

Le "Hello world !"

Comme la tradition le veut, commençons par le fameux "Hello World !". Les explications viendront ensuite.

using System ;  // Ceci est un commentaire sur une ligne !

namespace Prog1
{
  class MainClass
  {
    /* 
    Ceci est un 
    autre commentaire 
    de plusieurs lignes !

  • /
public static void Main() { Console.WriteLine("Hello world !"); } } } Au premier abord, la source du programme paraît plus compliquée que son équivalent en C++. Il n'en est rien ; vous allez comprendre la logique des choses ;) La première ligne permet d'indiquer que l'on va utiliser les classes que contient le namespace System (le namespace parent). Un namespace, littéralement espace de noms, permet de classer des données de manière structurée. Dans notre exemple, la classe Console utilisée ici pour afficher une phrase est rangée dans l'espace de noms appelé System. Si vous omettiez de commencer le programme par "using System;", qui signifie que vous utiliserez dans le cours du programme des classes de System, vous devriez préciser pour chaque classe de System utilisée qu'elle provient de ce namespace : System.Console.WriteLine("Hello world !"); par exemple. Vous verrez et comprendrez plus tard comment faire tout cela ainsi qu'à quoi correspond le reste du code. Sachez pour l'instant qu'en C# le programme démarre avec la fonction Main (avec une majuscule contrairement au C++) et que si votre programme n'en contient pas, il ne peut pas être compilé puisqu'il n'a pas de point d'entrée. Les variables Une variable correspond à un emplacement mémoire qui va contenir une valeur que vous lui assignerez. Grâce aux variables vous rendez vos programmes dynamiques, ils peuvent s'exécuter à chaque fois différemment en fonction d'arguments. Le C# étant fortement typé tout comme le C++, vous créerez des variables dont vous préciserez le type. Si vous souhaitez par exemple stocker l'âge de l'utilisateur, l'âge étant un nombre entier vous créez une variable de type int (integer), soit entier ce qui signifie que vous ne pourrez y stocker que des nombres sans décimales codés sur 4 octets. Voici comment procéder pour la création d'une variable : <type> <nom>; Vous pouvez en créer autant que vous le voulez en une seule déclaration tant qu'elles sont de même type, et les initialiser (leur donner une valeur) directement à l'aide de l'opérateur d'affectation, le "=" : <type> <nom>, <nom2> = <valeur>, <nom3>, <nom4> = <valeur>; Pour revenir à l'exemple de la variable qui stockerait l'âge de l'utilisateur : int AgeUtilisateur; Pour prévoir toute erreur due à l'initialisation, les variables sont automatiquement initialisées par le constructeur à leur valeur par défaut lors de l'instanciation. Toute variable numérique possède initialement la valeur zéro, les chaînes de caractères sont vides, et les variables booléennes sont à false. Voici maintenant une liste complète des type de variables : byte entier non-signé codé sur 8bits sbyte entier signé codé sur 8bits char entier non-signé codé sur 16bits (caractères unicodes) short entier signé codé sur 16bits ushort entier non-signé codé sur 16bits int entier signé codé sur 32bits uint entier non-signé codé sur 32bits long entier signé codé sur 64bits ulong entier non-signé codé sur 64bits float flottant signé codé sur 32bits double flottant signé codé sur 64bits decimal flottant signé codé sur 128bits bool booléen codé sur 8bits string chaîne de caractères unicodes (sur 16bits) object classe mère de toute les autres Comme en C++, quand on travail avec des caractères, des char, on les met entre apostrophes ('x') par contre, quand il s'agit d'une chaîne de caractère, un string, on met la chaîne de caractères entre guillemets ("xyz"). Une variable de type booléen peut être à deux états : soit false (par défaut), soit true. Elle est utile quand vous avez besoin de tester quelque chose qui ne peut être que vrai ou faux. Un langage fortement typé Quand on parle de langage typé, cela signifie que chaque variable, fonction etc doit porter un type. En effet, on créera par exemple une variable de type différent si l'on souhaite stocker un nombre entier, un caractère, voire une chaîne de caractères ou autre. Il existe des langages où il n'est pas nécessaire de typer nos éléments, ce qui non seulement nuit à la compréhension du code, mais aussi rend notre programme bien plus lent à l'exécution (les conversions devant être faites au pied levé). Dans vos futurs programmes, vous serez dans l'obligation de convertir des variables d'un type vers un autre explicitement. Imaginez que vous venez de stocker dans une variable de type int (entier) l'âge de Toto et que vous souhaitez maintenant utiliser cette variable en tant que paramètre d'une fonction qui attend un string (chaîne de caractères), qu'est ce que vous faîtes ? Il existe plusieurs moyens pour convertir une variable vers un autre type (notamment la classe System.Convert), mais convertir vers un string est nettement plus aisé car tous les types héritent de la classe Object qui contient une méthode ToString() qui renvoie la conversion d'un type X vers un string (compris ;) ?). Je sais ce n'est pas très compréhensible au premier abord mais voyez par vous même : int AgeToto = 55; string sAgeToto = AgeToto.ToString(); Console.WriteLine(sAgeToto); // Va afficher 55 dans une fenêtre de commande A la limite dans cet exemple vous ne seriez pas obligé de convertir, la fonction Console.WriteLine étant surchargée on peut lui passer tout un tas de différents types en argument. Bon, comme vous pouvez le voir dans cet exemple, on convertit une variable de type entier vers une chaîne de caractère en appelant la méthode (fonction) ToString(). Celle-ci fait partit de la classe System.Int32, qui elle-même hérite de System.Object (reportez-vous à la section relative à la programmation objet pour comprendre) dont le type est int et est donc accessible en utilisant le point ".", soit NomVariable.ToString(). Pour les conversions vers d'autres types, vous allez devoir utiliser une classe bien spécifique : System.Convert. Cette classe permet de convertir n'importe quel type vers un autre pourvu que la conversion soit possible. Vous voulez convertir une chaîne de caractère vers un nombre à virgule ? Rien de plus simple : string sPI = "3.1416"; float PI = System.Convert.ToSingle(sPI); // Au choix : System.Single.Parse(sPI); Reportez-vous à la documentation du framework pour connaître chaque méthode de conversion. Les fonctions Une fonction permet de réunir toute une série d'instructions à exécuter en bloc lors de son appel. Cela permet de diviser un programme en "sous-programmes", soit de diviser votre source en plusieurs fragments de codes (des fonctions) qui pourront être appelés n'importe quand pour effectuer leur corps (le code situé entre leurs accolades ouvrantes et fermantes). Une fonction peut attendre des arguments, qui permettent de modifier son comportement, ce qui la rend très dynamique. Les fonctions sont typées, tout comme les variables car une fois leur travail effectué elles vont pouvoir renvoyer une valeur (à moins qu'elles soient de type void, qui signifie qu'elles ne renvoient rien). Voici le squelette d'une fonction : <type> <nom> ( <type> <argument>, <type> <argument2>, <etc.> ) { <corps de la fonction> <renvoi d'une valeur suivant le type> } Imaginons alors une fonction qui nous permette de calculer la somme de deux valeurs. On passera en premier argument le premier des deux nombres et le second juste après. Dans le corps de la fonction on calculera leur somme et renverra le résultat : int Add( int A, int B ) { int Somme = A+B; return Somme; // Ou directement return A+B; } L'appel à la fonction se fait alors par son nom, puis en plaçant deux nombres entiers quelconques entre parenthèse. Mais comment récupérer la somme de A et B qui est renvoyée à la fonction ? Voici la solution : int Somme = Add(5,6); Cela aura pour effet d'exécuter la fonction avec pour argument les nombres 5 et 6, puis de stocker la valeur renvoyée par la fonction, c'est-à-dire 11 dans cet exemple, dans la variable Somme. Il est probable que vous souhaitiez créer des fonctions dont le corps exécute une série d'actions, mais qui ne nécessite pas le renvoi d'une quelconque valeur. Il existe pour cela un mot-clé, void, qui permet de créer une fonction « sans type », une procédure : void Affiche(string s) { System.Console.WriteLine(s); } Les tableaux A l'aide des tableaux vous allez pouvoir lier un certain nombre de variables de même type en une liste. On accède ensuite à un élément de cette liste à l'aide du numéro de son emplacement. Pour créer un tableau, on utilise l'opérateur new qui permet d'allouer de la mémoire. Pour les programmeurs C++, je vous rassure vous n'avez plus besoin de libérer la mémoire ! Le Garbage collector, soit Ramasse miette (ou même ramasse ordure), s'occupe automatiquement de tout ce qui a trait à de la desallocation. Tableaux unidimensionnels Voici la syntaxe à suivre pour créer un tableau à une dimension : <type>[] <nom> ; <nom> = new <type> [ <nombre éléments> ] ; Ou plus simplement : <type>[] <nom> = new <type> [ <nombre éléments> ] ; Pour créer un tableau qui pourra stocker dix noms cela donnera : string[] NomUtilisateurs = new string[10] ; On accède ensuite à un élément n du tableau de cette manière : <nom> [ n-1 ] . Pourquoi n-1 ? Cela vient de l'assembleur : Le premier élément du tableau n'est pas 1 mais 0, si l'on veut donc accéder au deuxième élément il faut mettre 1 entre crochets, d'où le n-1. Pour afficher le premier nom qui est stocké dans notre tableau NomUtilisateur nous procéderons ainsi : Console.WriteLine(NomUtilisateurs[0]) ; Tout comme les variables "normales", vous avez la possibilité d'initialiser les éléments de votre tableau dès sa création : string[] NomUtilisateur = new string[3] { "Robert", "Jean", "Toto" } ; Le C# permet même de simplifier cette déclaration en ne précisant pas le nombre d'éléments du tableau et en laissant le compilateur "compter" le nombre d'éléments entre accolades : string[] NomUtilisateur = { "Robert", "Jean", "Toto" } ; Tableaux rectangulaires Voilà maintenant que vous savez utiliser des tableaux à une dimension nous allons voir les tableaux multidimensionnels rectangulaires et non réguliers. Imaginons que nous souhaitions créer un agenda (agenda primitif je vous rassure) nous pourrions créer un tableau de 365 éléments pour les jours, qui chacun contiennent 24 éléments pour les heures, et qui eux-mêmes contiennent 60 éléments pour les minutes. Cela est tout à fait faisable de cette manière : string[,,] Calendrier = new string[365,24,60] ; Pour atteindre l'élément correspondant au 5 janvier à 13h50 on va alors faire : Calendrier[4,12,49] . La aussi vous avez la possibilité d'initialiser les éléments du tableau dès sa création : double[,] RectTab = { {1.0,2.0,3.0},{4.0,5.0,6.0} } ; Qui créera un tableau de 2 * 3 éléments, initialisés de 1.0 à 6.0 (valeurs double). Tableaux multidimensionnels non réguliers Voyons maintenant les tableaux multidimensionnels non réguliers, soit jagged en anglais. Ce genre de tableaux vous permet de créer des dimensions irrégulières car ce sont en fait des tableaux de tableaux. Le premier éléments d'un tableau peut contenir deux cases alors que le second en contiendrait cinq. Voici un exemple de création : int[][] NonRegTab = new int[2][] ; NonRegTab[0] = new int[2]; NonRegTab[1] = new int[5]; Là aussi vous avez la possibilité d'initialiser les éléments à la création : int[][] NonRegTab = new int[][] { new int[] {1, 2}, new int[] {3,4,5,6,7} } ; Pour afficher le contenu du troisième élément du deuxième tableau, procédez ainsi : Console.WriteLine(NonRegTab[1][2]) ; Les boucles et les conditions Les boucles permettent de répéter une série d'actions un certain nombre de fois alors que les conditions permettent d'exécuter une action en fonction d'un ou plusieurs tests préalables. Cela rend vos programmes d'autant plus dynamiques qu'ils peuvent analyser les variables et réagir de manière totalement différente. Il existe plusieurs types de tests ainsi que de boucles mais avant de les voir en détails, vous aurez besoin de connaître les différents opérateurs du C#. Ces derniers permettent soit de comparer des valeurs, soit de faire des opérations mathématiques. Ci-dessous la liste des opérateurs, suivie des différents tests et boucles. Liste des principaux opérateurs : = affectation + addition - soustraction
  • multiplication
/ division % modulo (reste de la division) ++ incrémentation (x = x+1) -- décrémentation (x = x-1) += addition d'une valeur (x = x+valeur) -= soustraction d'une valeur (x = x-valeur)
  • = multiplication avec une valeur (x = x*valeur)
/= division avec une valeur (x = x/valeur) & et (and) && et (and) | ou (or) || ou (or) ! négation (not) ?: opérateur de condition (c.f. section suivante) == égal != différent < inférieur > supérieur <= inférieur ou égal >= supérieur ou égal Comme vous l'avez sûrement remarqué, pour le OU et le ET binaire, il y a deux possibilités : le caractère simple ("&" et "|") ou le caractère doublé ("&&" et "||"). Les deux sont corrects, mais l'opérateur "doublé" est optimisé pour les tests. En effet, dans le cas d'un test avec && par exemple, si le premier élément en argument renvoie false, le deuxieme élément ainsi que ceux qui suivent ne seront pas évalués d'où un gain précieux de temps. Même démarche pour le OU binaire doublé "||" : si le premier élément renvoie true, à quoi bon évaluer les suivants puisque le premier est correct ? Pour les opérations mathématiques plus complexes, jetez un coup d'oeil à la classe Math de l'espace de nom System. Le test if Le mot-clé if permet d'analyser une expression booléenne (qui renvoie false ou true) puis d'exécuter une section de code différente en fonction du résultat. Attention, la conversion implicite des types numériques vers des booléens comme en C++ n'est pas applicable (if(3) provoquera une erreur). Voici la syntaxe du if : if( <expression booléenne> ) { // si <expression booléenne> est true fais ceci } else if( <expression booléenne 2> ) { // si <expression booléenne> est false mais que <expression booléenne 2> est true fais ceci // On peut évidemment ajouter autant de else if que nécessaire } else { // si tout ce qui précède est false, fais cela } Pour tout cela, les accolades ne sont nécessaires qu'à partir du moment où le nombre d'instructions dépasse un. Voyons maintenant un exemple concret : int AgeToto = 5, AgeFrereToto = 10; if(AgeToto == AgeFrereToto) { System.Console.WriteLine("Toto à le même âge que son frère !"); } else if(AgeToto > AgeFrereToto) { System.Console.WriteLine("Toto est plus âgé que son frère !"); } else { System.Console.WriteLine("Toto est le plus jeune..."); } Affichera dans une fenêtre console : "Toto est le plus jeune...". Il existe un opérateur qui permet de faire tout cela en une ligne (ce qui nuit aussi à la lisibilité du code) : c'est l'opérateur ?: : <condition> ? <true> : <false> ; Le test switch Le test switch permet d'évaluer une variable, puis de la comparer à une série de constantes afin d'exécuter du code en conséquence : switch( <variable à évaluer> ) { case <constante 1> : // si <variable à évaluer> égale <constante 1> effectue le code jusqu'à break break; case <constante 2> : // si <variable à évaluer> égale <constante 2> effectue le code jusqu'à break break; default : // si <variable à évaluer> n'est égale à aucun des cas effectue le code jusqu'à break break; } Le break permet de sortir du groupe switch. En c#, contrairement au C++, on n'a pas la possibilité de travailler en cascade, soit d'exécuter les cas consécutivement jusqu'à la rencontre d'un break. Pour passer d'un cas à l'autre, on utilise le mot-clé goto suivi du point d'arrivée. Voici un exemple : int AgeToto = 10; // et woui, il a grandi... switch(AgeToto) { case 4: System.Console.WriteLine("Il vient de perdre une année !"; goto default; case 5: System.Console.WriteLine("Il n'a pas grandit depuis le test if de tout à l'heure..."; break; case 10: System.Console.WriteLine("Il a maintenant le même âge que son frère lors du test if !"; break; default: System.Console.WriteLine("Son âge n'a rien de très particulier..."; break; } Cela affichera : "Il a maintenant le même âge que son frère lors du test if !" ! La boucle while Elle correspond à une boucle de type tant que. Vous passez en argument à la boucle une condition. Tant que celle-ci est respectée (tant qu'elle renvoie true), le corps de la boucle est répété : while( <condition> ) { // Les accolades sont nécessaires à partir du moment où le corps de la boucle dépasse une instruction */ <corps> } Voici un exemple qui vous permet de faire cinq tours de boucle : int i = 0; while( i<5 ) { // Corps de la boucle i++; // i = i+1 ; } Dans cet extrait de code, à chaque tour de boucle, la variable i augmente de un. La condition de la boucle étant i doit être plus petit que 5, la boucle s'arrêtera dès que i dépasse 4, donc après le 5ème tour de la boucle. La boucle for Cette boucle permet d'effectuer trois opérations en une déclaration : for( <expressions 1> ; <expression 2> ; <expression 3> ) { <corps> } L'expression 1 n'étant effectuée qu'avant l'entrée dans la boucle, elle est très utile par exemple pour créer une variable ou en initialiser une. L'expression 2 est la condition tant que de la boucle. Tout comme la boucle while, la boucle continue tant que cette condition reste correcte. L'expression 3 est exécutée à la fin de chaque passage de la boucle. Elle permet par exemple d'incrémenter une variable. Voici un segment de code qui équivaut à l'exemple de la boucle while : for(int i = 0 ; i < 5 ; i++) { // Corps de la boucle } La boucle do while Cette boucle fonctionne de la même manière que la boucle while, excepté qu'elle s'exécute au moins une fois, que la condition soit respectée ou non. On peut plus simplement dire que le corps de la boucle s'exécute avant le test : do { <corps> } while( <condition> ) ; Cette boucle par exemple de ne s'exécutera qu'une fois : int i = 3 ; do { i--; } while( i>5 ) ; La boucle foreach Cette boucle est assez spéciale. Elle vous permet d'itérer à travers un tableau ou une collection sans se préoccuper du nombre d'éléments. Une copie en lecture seule de l'élément courant du tableau est faite dans <variable> à chaque tour de boucle : foreach(<type> <variable> in <tableau>) { <corps> } Dans le corps vous manipulez l'élément courant du tableau à l'aide de la variable <variable>. int[] iTab = {1, 3, 5, 7, 9}; foreach(int i in iTab) { Console.WriteLine(i); } Les opérateurs de boucles En C#, vous disposez de deux opérateurs de boucles. L'un d'eux, break (utilisé aussi dans le test switch), permet de quitter une boucle pendant son exécution même si la condition est toujours respectée. Le second, continue, vous permet de retourner à la condition de la boucle sans passer par la suite du code de la boucle. Voici un exemple : for(int i=200, j=0; i>0; i--, j++) { if(i==j) break; else if(j*j == i) continue; } Cela se passe de commentaires... Les classes et les objets Il est largement temps d'aborder du concret maintenant : la programmation orientée objet, soit POO. Etant un sujet très complexe (il existe des livres complets rien qu'à ce sujet), je ne vais faire que vous en donner un très bref résumé et à vous la liberté d'aller sur google chercher des tutoriaux (il y en à même en français !) pour une meilleure compréhension. La programmation objet permet de hiérarchiser les données, de regrouper variables et fonctions (méthodes) au sein d'un même groupe appelé classe, avec la possibilité de les sécuriser à l'aide d'une série d'étiquettes disponibles (cela s'appelle l'encapsulation). Les données membres (faisant partie d'une classe) ne sont alors accessibles que par les éléments qui ont le droit d'y accéder, et sont invisibles pour les éléments externes à la sélection. La POO permet aussi, et c'est là son principal atout, de créer une classe dérivée, en d'autres termes de créer une classe qui hérite de tous les éléments de sa classe mère. On peut dès lors préciser notre nouvelle classe soit en modifiant ou en remplaçant les éléments déjà existants, soit en y ajoutant des nouveaux membres. Vous êtes sûrement déjà au courant : le C# est un langage totalement orienté objet. Sur d'autres sites on parle de 99% orienté objet mais ne voyant pas trop en quoi consiste le 1% restant je préfère parler de 100% ce qui rend tout plus simple, n'est-il pas ;) ? Tout comme le JAVA, on ne peut pas créer de fonctions ou variables globales en C#. Toutes les données doivent être englobées dans des classes, rien ne peut être placé en extérieur. Toutes les classes dérivent de la classe System.Object qui est la classe de base du framework. Pour ceux d'entre vous qui ont fait du C++, tout cela va donc peut-être vous sembler étrange mais je vous assure que l'on s'y habitue vite et on se rend même compte que c'est avantageux. Bon je commence un petit peu à m'impatienter là, passons à la pratique. Tout débute en créant une classe, dans laquelle nous réunirons des variables et fonctions (les données membres). Voilà comment créer une classe : class <nom classe> { <étiquette> <modificateur> <type> <nom variable membre>; // Et autant de variables qu'il vous est nécessaire public <nom classe> // Constructeur { <corps> } // Et autant de surcharges qu'il vous est nécessaire ~<nom classe> // Destructeur { <corps> } <étiquette> <modificateur> <type> <nom fonction membre> ( <arguments> ) { <corps> } // Et autant de fonctions (et de surcharges) qu'il vous est nécessaire } Commençons par voir ensemble à quoi correspond l'étiquette. Comme je vous l'ai dit, vous avez bien évidemment lu un tutorial sur la POO ce qui me dispense de vous refaire la théorie. Voici donc les trois étiquettes principales permettant de protéger vos données : public : Un membre <public> est accessible depuis n'importe qu'elle fonction, qu'elle fasse ou non partie de la même classe. Une donnée publique n'a aucune protection. protected : Un membre <protected> n'est accessible qu'à partir de la classe à laquelle il appartient, ainsi qu'aux classes dérivées (qui en héritent). Une classe B qui hérite de A aura donc accès aux variables et fonctions membres protégées de A. private : Un membre <private> subit le niveau de protection le plus élevé. Il n'est accessible que depuis sa classe et même une classe dérivée n'y a pas accès. Voyons maintenant ce qu'il en est du <modificateur>. Nous allons pour l'instant en aborder que un, mais nous étofferons tout cela plus tard. Voilà donc le tant attendu modificateur static : static : Vous permet de rendre une fonction ou une variable statique (Shared en VB.NET). Un membre statique est partagé entre chaque instence de la classe : si vous créez une variable V statique membre de la classe A, et que vous en créez deux instances I1 et I2, les deux instances accéderont à la même variable V. Pour accéder à une variable statique on utilise donc pas le handle de l'instance, mais le nom de la classe. Voici un exemple : class A { private static int _V = 0; public A() {} public void IncremV() { // this._V++; FAUX A._V++; } public void AugmenteV(int V) { // this._V += V; FAUX A._V += V; } /* Programme non compilable puisqu'il ne contient pas de fonction Main. Il n'est là que pour montrer un exemple d'utilisation de static */ } Je ne vais pas réexpliquer ce qu'est un type de variable ou de fonction, si cela est toujours abstrait pour vous, relisez le tout depuis le début ;) . Voyons maintenant un exemple complet et fonctionnel de programme qui utilise une bonne partie des notions abordées jusqu'à maintenant : namespace Prog2 { using System; class Utilisateur { private string _Nom; private int _Age; private char _Sexe; public Utilisateur(string Nom, int Age, char Sexe) { _Nom = Nom; _Age = Age; _Sexe = Sexe; } public void DefInfos(string Nom, int Age, char Sexe) { _Nom = Nom; _Age = Age; _Sexe = Sexe; } public string Infos() { string Ret = ""; Ret = "Cet utilisateur s'appelle " + _Nom + ".\r\n"; if(_Age > 0 && _Age < 100) // si l'âge est compris entre 0 et 100 ans Ret += "Il a " + _Age.ToString() + "ans.\r\n"; else // Sinon Ret += "Son âge laisse à penser qu'il n'est pas humain...\r\n"; switch(_Sexe) // On évalue _Sexe { case 'm': // Si il égale 'm' case 'M': // ou bien 'M' Ret += "Il est de sexe masculin !"; break; case 'f': case 'F': Ret += "Il est de sexe féminin !"; break; default: Ret += "C'est un eunuque..."; break; } return Ret; } } class MainClass { public static void Main(string[] Args) { Utilisateur[] Utilis = new Utilisateur[5]; // On crée un tableau d'Utilisateurs for(int i=0; i<Utilis.Length; i++) // On instencie chaque élément { Utilis[i] = new Utilisateur("Toto" + i.ToString(), i + 98, 'f'); } foreach(Utilisateur u in Utilis) { Console.WriteLine(u.Infos()); } } } } Constructeurs et Destructeur Qu'en est-il du constructeur et destructeur ? Les classes ont la possibilité d'avoir un constructeur et un destructeur autres que ceux présents par défaut. Le constructeur vous permet d'avoir la main lors de l'instanciation (création) de l'objet (initialisation de variables, etc) et le destructeur est appelé à sa destruction. Pour le destructeur, c'est le garbage collector qui s'occupe de l'appeler on ne sait donc pas exactement quand il va le faire. On reconnaît un constructeur par le fait qu'il porte le même nom que la classe où il est créé. De même pour le destructeur, sauf qu'il est précédé du symbole "~" (tilda) qui permet de le différencier des constructeurs. Ces derniers n'ont pas de genre, pas même void. Le constructeur doit porter l'étiquette public pour être utilisable, le fait de le protéger avec une autre étiquette va interdire toute instance de la classe. Le destructeur quant à lui n'a aucune étiquette. Pour instancier une classe vous procéderez ainsi : new MaClasse(); // Appel du constructeur de MaClasse sans argument Vous créez un objet de type MaClasse : vous appelez le constructeur et allouez de l'espace en mémoire pour votre objet. Allouer de la mémoire c'est bien joli mais encore faut-il savoir où se trouve l'allocation en mémoire pour pouvoir en profiter. On utilise donc un handle (du type de la classe), une sorte de lien vers l'objet en mémoire : MaClasse m = new MaClasse(); // Où m est le handle de l'instance Revenons-en a nos moutons. Le constructeur par défaut est sans argument et vous avez la possibilité de le redéfinir, ainsi que de le surcharger pour qu'il puisse attendre une autre série d'arguments. Nous pouvons par exemple faire ceci : class MaClasse { private int MaVar ; public MaClasse() // Constructeur sans argument { MaVar = 5 ; } public MaClasse(int v) // Constructeur surchargé qui attend un entier { MaVar = v ; } ~MaClasse(){} } Et vous avez alors deux possibilités pour créer un objet : - MaClasse m1 = new MaClasse() ; qui va initialiser MaVar à 5 - MaClasse m2 = new MaClasse(3) ; qui va initialiser MaVar à 3 Les Accesseurs En programmation objet, on a pour habitude de cacher toute variable membre, les rendre invisibles à l'interface (privées), et de définir ensuite un accesseur aux données en questions. Les données peuvent être : - accessibles en lecture - accessibles en écriture (plutôt rare) - accessibles en lecture et en écriture - inaccessibles (au quel cas il suffit de ne rien faire ;) Nous pourrions tout simplement procéder ainsi : private int _Var ; // Invisible depuis l'extérieur public void SetVar(int v) { if(v > 0) // Test quelconque si nécessaire _Var = v; } public int GetVar() { return _Var; } Cela serait la méthode C++. Mais le C# nous offre un autre moyen de définir l'accès (proche du java et du delphi), à l'aide de get et set : private int _Var ; public int Var { get{return _Var ;} set{if(value > 0) _Var = value ;} // value est un mot-clé qui fait référence à la valeur passée a Var } De cette manière l'utilisateur peut lire et modifier la variable privée _Var en passant par Var. Revoyons maintenant notre dernière source. A priori, l'utilisateur n'est pas censé changer de nom ou de sexe (je ne parle pas des exceptions ;) par contre son âge va augmenter chaque année. On va alors définir la variable privée qui stocke l'âge en lecture/écriture et le reste en lecture seule : namespace Prog2 { using System; class Utilisateur { private string _Nom; private int _Age; private char _Sexe; public string Nom { get{return _Nom ;} } public char Sexe { get{return _Sexe ;} } public int Age { get{return _Age ;} set{ if(value > 0 && value < 110) // Si l'âge est compris entre 0 et 110 (je suis effectivement très optimiste) _Age = value; } } public Utilisateur(string Nom, int Age, char Sexe) { _Nom = Nom; if(Age > 0 && Age < 110) _Age = Age; else _Age = -1; if(Sexe == 'm' || Sexe == 'M' || Sexe == 'f' || Sexe == 'F') _Sexe = Sexe; else _Sexe = 'x'; } } class MainClass { public static void Main() { Utilisateur Utilis = new Utilisateur("Henri", 2, 'm') ; // Un an plus tard Utilis.Age ++ ; // permis car lecture/écriture // Une envie subite de changer de sex. Utils.Sexe = 'f'; // INTERDIT car en lecture seule (provoque une erreur de compilation) Console.WriteLine(Utilis.Nom) ; // permis car lecture autorisée } } } L'héritage L'héritage vous permet de créer une nouvelle classe sur le modèle d'un parent. La classe fille va hériter de tous les membres de sa classe mère, et vous allez être en mesure d'ajouter de nouveaux membres (variables et fonctions) ainsi que de redéfinir des fonctions existentes dans le but de préciser votre classe. Voici la syntaxe de l'héritage : class <nom fille> : <nom mère> { // Contient alors tous les membres de <nom mère> } Pour obtenir une classe fille de Object, cela donnera ceci : class ExtObject : Object { } Le C# définit une classe mère de toute classe existante, la classe Object. A partir du moment ou vous déclarez une classe, que vous précisiez un parent ou non, elle sera assurément fille de Object. La déclaration suivante est donc tout à fait équivalente à l'exemple précédent : class ExtObject2 // : Object implicite { } Vous pouvez bien entendu créer autant de générations nécessaires : class c1 { // Contient les membres de Object } class c2 : c1 { // Contient les membres de Object et c1 } class c3 : c2 { // Contient les membres de Object, c1 et c2 } On peut alors dire que c3 est un c2, est un c1, et est un Object. Cela vous caster vos objets de la sorte : c3 C = new c3(); Object O = C; // C (de type c3), EST un Object C = null; C = (c3)O; // retour vers le type d'origine Vous allez constamment devoir redéfinir des fonctions d'une classe parente pour les adapter à la situation. La classe Object (mère implicite de toute classe), possède, entre autres, la fonction ToString(). Vos classe posséderont donc assurément cette fonction virtuelle (pouvant être redéfinie) : class Vivant { public static void Main() { Vivant v = new Vivant(); Console.WriteLine(v.ToString()); } } Malheureusement, la chaîne de caractères affichée en Console (le nom de la classe : "Vivant") n'est pas forcément ce que nous aurions souhaité avoir sous les yeux. Redéfinissons donc cette fonction (possible puisqu'elle est virtuelle) : class Vivant { public override string ToString() // Nottez le mot-clé override qui est un <modificateur> au même titre que static { return "Ceci est un être vivant !"; } public static void Main() { Vivant v = new Vivant(); Console.WriteLine(v.ToString()); } } Vous obtenez maintenant en console la phrase "Ceci est un être vivant !", nette amélioration. Ceci est dû à la redéfinition de la fonction virtuelle ToString() à l'aide du modificateur override. L'appel de Vivant.ToString() n'appellera plus la fonction de la classe parente (Object) mais bien celle de la classe Vivant. Il vas dorénavent vous être nécessaire de créer vous même des fonctions virtuelles, dans le but de prévoir à une modification ultérieure lors d'une dérivation de votre classe. C'est le mot-clé virtual qui remplit cette tâche. La classe Object définit donc une fonction dont la signature est celle-ci : public virtual string ToString(); Et vous n'avez pas besoin de repréciser à chaque redéfinition de la fonction qu'elle est virtuelle. Le modificateur override implique que la fonction soit à la base virtuelle, et qu'elle le reste chez les descendants. [...] ----------------------------------------------------- Adrien T. smi_NO-SPAM_kar@caramail.com [enlever _NO-SPAM_ pour l'e-mail fonctionnel]

Conclusion :


J'essaie d'y ajouter régulièrement des choses bien que le temps me manque. Si tout va bien il devrait tout de même grossir de quelques lignes chaque mois...

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.