.. _IntroCSharp: Base du langage C# ****************** C# est un langage introduit par Microsoft en 2000. C'est un langage objet avec un typage statique fort, une syntaxe héritée du C/C++ et une philosophie très proche de Java. C# est un langage phare du framework .net qui se popularise pour la conception de sites Web (ASP), d'ERP (Sharepoint), de scripting et d'applications lourdes. A l'origine considéré comme une pâle copie de Java, le C# a ensuite bénéficié d'une politique de développement très dynamique par rapport à Java, resté en désérence plusieurs années par Sun avant son rachat par Oracle. Aujourd'hui, C# est un langage de programmation moderne avec une bibliothèque standard très riche et des outils de développement avancés. La syntaxe du C# est très proche du C et de Java et ne devrait donc pas poser de difficulté si vous avez déjà pratiqué l'un de ces langages. Les éléments de base de la structuration du code sont les accolades ``{}``, qui définissent les blocs, et le point virgule ``;``, qui marque la fin d'une instruction. Le saut de ligne est un caractère d'espacement comme un autre (une instruction peut s'étaler sur plusieurs lignes). Les plus courageux peuvent consulter le `guide officiel de programmation C# `_ et les `spécifications complètes du langage. `_ Les commentaires ================ Les **commentaires** sont introduits commme en C/C++ et Java : * sur une seule ligne: tout ce qui suit les caractères ``//`` est ignoré jusqu'à la fin de la ligne; * sur plusieurs lignes: tout ce qui se trouve entre les balises ``/*`` et ``*/`` est ignoré. .. code-block:: csharp // Je suis un commentaire sur une seule ligne. /* Je suis un commentaire sur plusieurs lignes. J'apparais grisé dans les éditeurs offrant une coloration syntaxique */ Les variables ============= Un programme stocke l'information dans des **variables**. Une variable correspond à un espace de la mémoire auquel on donne un nom, on parle d'**identificateur**, et auquel on associe un **type de donné**. Le contenu de cet espace mémoire peut alors être manipulé (lu ou écrit) en le désignant par son identificateur. Déclaration ------------ Avant de pouvoir utiliser une variable il faut la **déclarer** avec la syntaxe suivante : .. admonition:: Syntaxe .. code-block:: csharp TypeDeDonnee Identificateur; TypeDeDonnee Identificateur = ValeurInitiale; Exemples : .. code-block:: csharp int unEntier; float unNombreFloatant = 3.0; // déclaration de la variable unNombreFloatant de type float et initialisation Bitmap uneImage; // déclaration de la variable uneImage de type Bitmap .. warning:: Une variable peut être déclarée sans être **initialisée** : sa valeur est alors indéfinie. Essayer de lire une variable alors que sa valeur est indéfinie est une erreur de compilation en C#. Un identificateur est un nom donné par l'utilisateur et il est fortement conseillé de choisir un nom explicite, c'est à dire qui décrit le rôle de la variable. La seule exception à cette règle concerne les variables de boucle pour lesquelles les identificateurs courts ``i``, ``j``, ``k`` sont réservés. Ces aspects sont traités en détail dans les `recommendations générales de nommage `_ du guide de programmation C#. .. code-block:: csharp int a = 20; // à éviter : impossible de voir à quoi correspond cette variable à partir de son nom int largeurPixel = 20; // beaucoup mieux: on sait immédiatement que la variable correspond à une largeur // et que l'on compte en nombre de pixels Les identificateurs ne peuvent pas être des `mots clés `_ du langage (``int``, ``if``, ``else``...). Ils sont sensibles à la casse (majuscule, minuscule) et doivent obéir à certaines règles : ne pas commencer par un chiffre, ne pas contenir certains signes... (`l'ensemble des règles `_ est plutôt complexe, mais en pratique tenez vous en aux caractères alphanumériques et tout ira bien). .. note:: En C#, on suit la **convention** `Camel Case `_ : les différents mots constituant un identificateur commencent par une majuscule et ne comportent pas de caractère de séparation. De plus, pour les identificateurs de variables, le premier mot commence par une minuscule. Pour plus d'information sur le sujet, voir `l'utilisation des majuscules `_. dans le guide de programmation. .. code-block:: csharp int jeSuisLaNormeCamelCase; .. figure:: CamelCase.svg :scale: 30 % :align: center :alt: Camel Case Principe du Camel Case (source: wikipedia) .. quiz:: basesharp-nommageVariable :title: Identificateurs Pour chacune des déclarations de variables ci-dessous, dites si l'identificateur choisi semble approprié : 1) :quiz:`{"type":"TF","answer":"F"}` int rhs; #) :quiz:`{"type":"TF","answer":"T"}` int i; #) :quiz:`{"type":"TF","answer":"F"}` double solde_compte; #) :quiz:`{"type":"TF","answer":"T"}` double soldeCompte; #) :quiz:`{"type":"TF","answer":"F"}` bool 1Resultat; #) :quiz:`{"type":"TF","answer":"F"}` int NombreDeChats; Types de données ================ Le langage C# est * fortement typé : il est interdit de mettre une valeur dans une variable si les types de la variable et de la valeur ne sont pas compatibles; * manuellement typé : les informations de typage sont données par le développeur; * statiquement typé : le typage est vérifié par le compilateur et toute erreur empêche la compilation du programme; * dynamiquement typé : le CLR vérifie également l'intégrité des types à l'exécution. Chaque variable est associée à un type donné. Ces types sont divisés en 2 catégories: 1. Les types **valeurs** composés des **types primitifs** (types de base du langage) et des structures (non traitée dans ce cours). Pour cette catégorie, l'emplacement mémoire correspondant à la variable contient directement la valeur concernée. .. csv-table:: :header: "Type de données", "Genre", "Taille (octets)", "Valeurs possibles" :widths: 10, 10, 10, 20 ``byte``, nombre entier, 1, :math:`0` à :math:`255` ``short``, nombre entier, 2, :math:`-2^{15}` à :math:`2^{15}-1` ``int``, nombre entier, 4, :math:`-2^{31}` à :math:`2^{31}-1` ``long``, nombre entier, 8, :math:`-2^{63}` à :math:`2^{63}-1` ``float``, nombre réel, 4, :math:`\pm 1.5\times 10^{-45}` à :math:`\pm 3.4\times 10^{38}` ``double``, nombre réel, 8, :math:`\pm 5.0\times 10^{-324}` à :math:`\pm 1.7\times 10^{308}` ``decimal``, nombre réel, 16, :math:`-7.9\times10^{28}` à :math:`7.9\times10^{28}` ``bool``, valeur booléenne, 1, ``true`` ou ``false`` ``char``, caractère, 2, ``U+0000`` à ``U+FFFF`` (caractères Unicode) 2. Les types **références** composés des **classes** définies par l'utilisateurs ou présentes dans l'API (en particulier les tableaux). Dans ce cas l'emplacement mémoire correspondant à la variable contient une référence vers un objet. Nous reviendrons en détail sur le comportement de cette catégorie de types. Constantes littérales --------------------- Les **constantes littérales**, c'est-à-dire, les valeurs rentrées directement dans le code sont typées : .. code-block:: csharp 1 // est un entier (le type exacte byte, short, int ou long sera déteminé par le contexte) 1.0 // est un double 1.0f // est un float 'c' // est un caratère true // est un booléen Transtypage ----------- Le **transtypage** ou **cast** désigne l'action de transformer une valeur d'un type de données vers un autre type. Pour caster une valeur dans un nouveau type, il faut indiquer le nom du nouveau type entre parenthèses devant la valeur: .. code-block:: csharp double d = 2.1; int i = (int)d; // d est casté en int, i contient la valeur 2: la partie décimale est supprimée Si le transtypage n'est pas possible, il y aura une erreur, soit au moment de la compilation, soit au cours de l'exécution du programme. Le C# n'est pas un langage *très* fortement typé: le compilateur accepte de réaliser automatiquement certains transtypages si ces derniers peuvent se faire sans perte. On parle de **conversion implicite**. .. code-block:: csharp int x = 2; double d = x; // 2 est converti en double, le compilateur ne dit rien int y = d; // ERREUR, la conversion automatique de double vers int n'est pas possible Parmi les types primitifs, les conversions implicites suivantes sont possibles: ``double`` :math:`\leftarrow` ``float`` :math:`\leftarrow` ``long`` :math:`\leftarrow` ``int`` :math:`\leftarrow` ``short`` :math:`\leftarrow` ``byte``. .. attention:: Les opérations de convertions implictes par le compilateur peuvent parraitre annodines mais elles sont un élément clef d'un des composants les plus complexes des langages de programmation moderne: la **résolution de nom**. La `résolution de nom `_ consiste, pour le compilateur, à déterminer quel est l'identificateur correcte à utiliser en fonction du context. Ce mécanisme est très complexe dans les langages modernes, chaque couche d'abstraction générant de nouvelles règles (polymorsphime paramétriques, polymorphismes d'héritage, généricité, template, duck typing). Cela mène à des résultats surprenant lorsque l'on ne maitrise pas bien ces mécanismes. Par exemple, ruby et javascript sont connus pour leurs `conversions implicites hasardeuses `_ alors que les interractions entre conversions implicites et templates de la librairie standard du C++ génère des messages d'erreurs `interminables et difficilement compréhensibles `_ . Exercices ^^^^^^^^^ .. quiz:: basecsharp-vartype :title: Catégories de types Les 2 catégories de contenu pour les variables en C# sont :quiz:`{"type":"FB","answer":"valeur référence","flags":"fuzzy,sequence"}` .. quiz:: basesharpcast1 :title: Typage Pour chacune de ces affirmations, indiquez si elle est vraie ou fausse: 1) :quiz:`{"type":"TF","answer":"T"}` Le C# utilise du typage statique. #) :quiz:`{"type":"TF","answer":"T"}` Le C# utilise du typage dynamique. #) :quiz:`{"type":"TF","answer":"F"}` En C# il n'y a pas de vérification de type à l'exécution du programme. #) :quiz:`{"type":"TF","answer":"F"}` Un transtypage permet de changer le type d'une variable. #) :quiz:`{"type":"TF","answer":"F"}` On peut forcer n'importe quelle conversion avec un transtypage explicite. #) :quiz:`{"type":"TF","answer":"F"}` Le cast d'un ``double`` en ``int`` est réalisé avec une opération d'arrondi. .. quiz:: basesharpcast24 :title: Constantes et conversions implicites Pour chacune des déclarations/initialisations de variables, indiquez si elle est correcte ou fausse: .. csv-table:: :widths: 10, 10, 10 :delim: ! :quiz:`{"type":"TF","answer":"T"}` ``bool b1 = true;`` ! :quiz:`{"type":"TF","answer":"T"}` ``float f2 = 1.0f;`` ! :quiz:`{"type":"TF","answer":"F"}` ``short s3 = 1.0;`` :quiz:`{"type":"TF","answer":"F"}` ``bool b2 = 0;`` ! :quiz:`{"type":"TF","answer":"F"}` ``float f3 = 1.0;`` ! :quiz:`{"type":"TF","answer":"T"}` ``int i1 = 1;`` :quiz:`{"type":"TF","answer":"F"}` ``bool b3 = "true";`` ! :quiz:`{"type":"TF","answer":"T"}` ``double d1 = 1;`` ! :quiz:`{"type":"TF","answer":"F"}` ``int i2 = 1.0f;`` :quiz:`{"type":"TF","answer":"T"}` ``char c1 = 'b';`` ! :quiz:`{"type":"TF","answer":"T"}` ``double d2 = 1.0f;`` ! :quiz:`{"type":"TF","answer":"F"}` ``int i3 = 1.0;`` :quiz:`{"type":"TF","answer":"F"}` ``char c2 = 46;`` ! :quiz:`{"type":"TF","answer":"T"}` ``double d3 = 1.0;`` ! :quiz:`{"type":"TF","answer":"T"}` ``long l1 = 1;`` :quiz:`{"type":"TF","answer":"F"}` ``char c3 = "a";`` ! :quiz:`{"type":"TF","answer":"T"}` ``short s1 = 1;`` ! :quiz:`{"type":"TF","answer":"F"}` ``long l2 = 1.0f;`` :quiz:`{"type":"TF","answer":"T"}` ``float f1 = 1;`` ! :quiz:`{"type":"TF","answer":"F"}` ``short s2 = 1.0f;`` ! :quiz:`{"type":"TF","answer":"F"}` ``long l3 = 1.0;`` Expression ========== Une **expression** est une combinaison de variables, d'opérateurs, de constantes et d'appels de fonction. Comme les variables, les expressions ont une valeur et un type. Par contre les expressions ne sont pas identifiées par un nom. Le calcul de la valeur d'une expression est appellé **évaluation**. .. code-block:: csharp int x = 2; int y = x + 2; // x + 2 est une expression de type int et de valeur 4 bool b = y >= 5; // y >= 5 est une expression de type bool et de valeur false Les **règles de priorité** classiques sont utilisées lors de l'évaluation d'une expression (par exemple la multiplication est prioritaire sur l'addition). Les **parenthèses** permettent de changer l'ordre d'évaluation des différentes parties d'une expression et de clarifier la lecture. Opérateurs simples ------------------ La `liste des opérateurs du C# `_ est assez longue, les plus communs sont: * Opérateurs de comparaison : ==, !=, <, >, <=, >= * Opérateurs logiques : || (ou), && (et), ! (négation) * Opérateurs numériques: +, -, \*, /, % (modulo) Lorsque les 2 opérandes numériques d'un opérateur binaire ne sont pas du même type, l'opérande du type le plus petit est automatiquement convertie vers le type le plus grand. Par exemple: .. code-block:: csharp 1.0 + 2 // équivalent à 1.0 + 2.0: 2 est converti en double Opérateur d'affectation ``=`` ----------------------------- L'**opérateur d'affectation** permet de modifier la valeur d'une variable (à gauche du signe égal) avec une nouvelle valeur (à droite du signe égal). On dit que les variables sont des **left-value** car elle peuvent être mise à gauche d'un signe égal: elles correspondent à une zone de mémoire modifiable. .. code-block:: csharp int x; x = 4; .. important:: L'opérateur d'affectation fonctionne toujours par copie. La variable reçoit une copie de la valeur à droite du signe égal. .. code-block:: csharp int x = 1; int y = 0; y = x; // y = 1 y = y + 1; // y = 2, x n'est pas modifié et vaut toujours 1 Dans le cas d'un type référence, la valeur stockée dans la variable étant la référence, l'opérateur d'affectation copie cette référence et non l'objet référencé. Il existe une variante de l'opérateur d'affectation pour chaque opérateur binaire. Par exemple l'opérateur dérivé ``+=`` permet d'ajouter une valeur à une variable: .. code-block:: csharp int x = 3; x += 2; // équivalent à x = x + 2; Remarque: Le type d'une expression d'affectation ``x = y`` est le type de ``x`` et sa valeur est la valeur de ``y``. Exercices --------- .. quiz:: basecsharpexpressiontypevaleur :title: Expressions numériques Pour chacune des expressions, donnez son type et sa valeur : .. csv-table:: :header: Expression, Type, Valeur :widths: 10, 10, 10 :delim: ! ``2 + 3 / 2`` ! :quiz:`{"type":"FB","answer":"int"}` ! :quiz:`{"type":"FB","answer":"3"}` ``2 + ( 5 % 2 )`` ! :quiz:`{"type":"FB","answer":"int"}` ! :quiz:`{"type":"FB","answer":"3"}` ``2 + ( -5 % 2 )`` ! :quiz:`{"type":"FB","answer":"int"}` ! :quiz:`{"type":"FB","answer":"1"}` ``2.0 + 3 / 2`` ! :quiz:`{"type":"FB","answer":"double"}` ! :quiz:`{"type":"FB","answer":"3"}` ``2 + 3.0 / 2`` ! :quiz:`{"type":"FB","answer":"double"}` ! :quiz:`{"type":"FB","answer":"3.5"}` ``5 <= 3 + 2`` ! :quiz:`{"type":"FB","answer":"bool"}` ! :quiz:`{"type":"FB","answer":"true"}` ``1.5f + 2`` ! :quiz:`{"type":"FB","answer":"float"}` ! :quiz:`{"type":"FB","answer":"3.5"}` ``1.5f + 2.0`` ! :quiz:`{"type":"FB","answer":"double"}` ! :quiz:`{"type":"FB","answer":"3.5"}` Structures de contrôle ====================== On retrouve les structures de contrôle classiques : **if**, **else**, **for**, **while**. if -- .. admonition:: Syntaxe .. code-block:: csharp if ( condition ) { // instructions à réaliser si CONDITION vaut true } else { // instructions à réaliser si CONDITION vaut false } for --- .. admonition:: Syntaxe .. code-block:: csharp for ( initialisation; condition; instructionDIteration ) { // bloc d'instructions à réaliser tant que condition vaut true } .. figure:: pour.svg :scale: 30 % :align: center :alt: Workflow boucle for Workflow d'exécution d'une boucle for while ----- .. admonition:: Syntaxe .. code-block:: csharp while ( condition ) { // bloc d'instructions à réaliser tant que condition vaut true } .. figure:: while.svg :scale: 30 % :align: center :alt: Workflow boucle while Workflow d'exécution d'une boucle while Les fonctions ============= Une **fonction** est une portion de code qui effectue une tâche ou un calcul. Une fonction est désignée par un identificateur qui permet de l’appeler à partir d’autres endroits du programme, c’est-à-dire de demander à exécuter la portion de code qui correspond à la fonction. La fonction peut retourner une valeur, c’est-à-dire **renvoyer un résultat**. Une fonction peut prendre des **arguments** ou **paramètres** qui désignent les données d’entrée sur lesquelles travaille la fonction. Déclaration ----------- La déclaration d'une fonction se fait en 2 parties : * On commence par donner son **prototype** qui contient toutes les informations d'identification de la fonction: son nom, ses paramètres, son type de retour... * Puis on donne son corps entre accolades qui contient les instructions de la fonction. .. admonition:: Syntaxe .. code-block:: csharp TypeDeRetour IdentificateurFonction(TypeParametre1 identificateurParametre1, TypeParametre2 identificateurParametre2, ...) // prototype { // début du corps // intructions de la fonction return valeurDeRetour; } // fin du corps Une fonction qui ne retourne pas de résultat utilise le type de retour special ``void`` (qui signifie rien). Les fonctions qui ne retournent rien sont appelées **procédures**. .. admonition:: Syntaxe .. code-block:: csharp void IdentificateurFonction(TypeParametre1 identificateurParametre1, TypeParametre2 identificateurParametre2, ...) { // intructions de la fonction return; // optionnel, remarquez l'absence de valeur après l'instruction return } .. attention:: Les fonctions dont le type de retour est différent de ``void`` doivent se terminer par l'exécution d'une instruction ``return`` suivie d'une valeur dont le type est compatible avec le type de retour de la fonction. Le compilateur vérifie que, quelque soient les paramètres d'entrée de la fonction, l'exécution de la fonction se termine toujours par une instruction ``return``. Ainsi le code suivant est faux et provoque le message d'erreur : *Tous les chemins de code ne retournent pas nécessairement une valeur* : .. code-block:: csharp int F3(int x) { if(x<2) { return 1; } } .. note:: Par convention, les identificateurs de fonction en C# suivent la norme Camel Case mais, contrairement aux identificateurs de variable, on fera commencer leur premier mot par une majuscule. Appel de fonction ----------------- Une fonction est appelée en indicant son identificateur suivi, entre paranthèses, des valeurs des paramètres: .. admonition:: Syntaxe .. code-block:: csharp IdentificateurFonction(valeurParamètre1, valeurParamètre2,...); Un **appel de fonction** dont le type de retour est différent de ``void`` est une expression qui a pour type le type de retour de la fonction et pour valeur la valeur retournée par la fonction. .. code-block:: csharp int Fonction2(int a) { a += 1; return a; } void Fonction1() { int b = 7; b = Fonction2(b); // Fonction2(b) est une expression de type int et de valeur 8 } .. important:: Lors de l'appel d'une fonction, le passage des paramètres est toujours réalisé par copie : la fonction reçoit une copie de la valeur passée en paramètre. Si la variable est de type valeur, il s'agit d'une copie de cette valeur. Si la variable est de type référence, il s'agit d'une copie de la référence. .. code-block:: csharp void Increment(int a) { a += 1; } ... int x = 0; Increment(x); int y = x; // y vaut 0 ! Portée des variables -------------------- Un **bloc** est une portion de code délimitée par une paire d’accolades : ``{}``. Les blocs sont organisés de manières hiérarchiques : chaque bloc peut contenir d'autres blocs et ainsi de suite. On parle de **bloc de niveau supérieur** (le contenant) et de **bloc de niveau inférieur** (le contenu). Les **variables** dites **locales** sont : * les paramètres d’une fonction, * les variables déclarées à l’intérieur d’une fonction, * les variables déclarées dans une boucle. Une variable locale a une **portée d’utilisation** limitée. Elle ne peut donc pas être utilisée à l’extérieur de sa portée, d’où le caractère local. Sa portée commence à sa déclaration/définition. Pour un paramètre de fonction ou une variable déclarée dans un bloc, la portée se termine à la fin de son bloc : lors de l’accolade fermante. Dans le cas d’une boucle, la variable cesse d'exister lorsque la boucle se termine. Exemple: .. code-block:: csharp :emphasize-lines: 5,13,15 int Fonction2(int a) { a += 1; return a; } // a cesse d'exister ici void Fonction1() { int b = 7; for(int i=0; i < 10; i++) { b = Fonction2(b); } // i cesse d'exister ici } // b cesse d'exister ici Exercices --------- .. quiz:: basecsharp-qcmfonction1 :title: Fonctions Pour chacune de ces affirmations, indiquez si elle est vraie ou fausse: 1) :quiz:`{"type":"TF","answer":"F"}` Toutes les fonctions doivent se terminer par une instruction ```return``. #) :quiz:`{"type":"TF","answer":"T"}` Une méthode ayant pour type de retour ``void`` est appelée une procédure. #) :quiz:`{"type":"TF","answer":"T"}` Lors d'un appel de fonction, la fonction appellée reçoit une copie des valeurs passées en paramètre. .. quiz:: basecsharp-qcmfonctionporteevariable :title: Portée des variables Le code ci-dessous contient 3 erreurs (on suppose que la méthode ``Afficher`` prenant un entier en paramètre existe). .. code-block:: csharp :linenos: void Toto(int a, int b) { int z = 7; for (int i = 0; i < b; i++) { int d = 11; Afficher(d + a); } for (i = 0; i < b; i++) { int e = 11; Afficher(e + d); } Afficher(a); Afficher(z); Afficher(e); } Les numéros de lignes contenant les erreurs sont :quiz:`{"type":"FB","answer":"9 12 16","flags":"sequence"}` Les tableaux ============ En utilisant un **type existant**, le langage permet de créer des **tableaux**. Un tableau peut être vu comme une liste, un vecteur ou une matrice d'éléments du même type. Chaque **case** ou **cellule** d'un tableau stocke un élément du type donné. Déclaration et création ----------------------- Etant donné un type ``T``, la notation ``T[]`` désigne le nouveau type **tableau d'éléments de type T**. Par exemple ``int[]`` désigne le type *tableau d'entiers* qui est différent du type *entier*. On peut déclarer des variables avec ce type de données. Pour créer un tableau, on utilise l'opérateur ``new``. Les tableaux sont de **type référence**. Tableaux unidimentionnels: .. admonition:: Syntaxe .. code-block:: csharp TypeDesElements [ ] monTableau1; // déclaration d'un tableau TypeDesElements [ ] monTableau2 = new TypeDesElements [tailleDuTableau]; // déclaration et création d'un tableau int [ ] monTableau3 = {1, 5, 6, 7}; // déclaration d'un tableau et initialisation explicite Tableaux multidimensionnels : .. admonition:: Syntaxe .. code-block:: csharp TypeDesElements [,] monTableau1; // déclaration d'un tableau bidimentionnel TypeDesElements [,,] monTableau2; // déclaration d'un tableau tridimentionnel TypeDesElements [,] monTableau3 = new TypeDesElements [nombreDeLignes, nombreDeColonnes] ; // déclaration et initialisation d'un tableau int [,] monTableau4 = { {1, 5, 6}, {2, 9, 7} }; // déclaration d'un tableau 2d et initialisation explicite Les variables de type tableau sont commes les autres : elles doivent être initialisées avant d'être utilisées. Lors de la création d'un tableau, chaque cellule du nouveau tableau est **initialisée par défaut**. Ainsi, si vous créez un tableau d'entiers, chaque case sera mise à zéro. Accès aux éléments d'un tableau ------------------------------- Les cases d'un tableau sont numérotées de 0 à la taille du tableau moins un. Le numéro d'une case est appelé son **indice** et permet d'accéder à cette case. .. admonition:: Syntaxe .. code-block:: csharp int [] monTableau1 = {1, 2, 3}; monTableau1[0] = 4; //modification de la valeur de la case d'indice 0: monTableau1 = {4, 2, 3} int a = monTableau1[2]; // lecture de la valeur de la dernière case du tableau: a = 3 int [,] monTableau2 = { {1, 2, 3}, {4, 5, 6}}; monTableau2[0, 0] = 7; // monTableau2 = { {7, 2, 3}, {4, 5, 6}} int b = monTableau2[1, 2]; // b = 6 L’accès à un indice invalide d'un tableau (en dehors d’un tableau) déclenche une erreur à l’exécution. Taille d'un tableau ------------------- Pour connaitre la taille d'un tableau unidimentionnel on utilise la propriété ``Length``: .. code-block:: csharp int [] monTableau1 = {1, 2, 3}; int taille = monTableau1.Length; // taille = 3 Pour connaitre la taille d'un tableau bidimentionnel on utilise la méthode ``GetLength(dimension)``: .. code-block:: csharp int [,] monTableau1 = { {1, 2, 3}, {4, 5, 6} }; int lignes = monTableau1.GetLength(0); // 2 int colonnes = monTableau1.GetLength(1); // 3 Parcours des tableaux --------------------- Avec une boucle **for** ou une boucle **foreach** (si les indices des éléments ne nous intéressent pas). .. code-block:: csharp bool [] monTableau = {true, true, false}; for (int i = 0; i < monTableau.Length; i++) { ... monTableau[i] ... } foreach (bool valeur in monTableau) // se lit: "pour toute valeur de type bool dans monTableau" { ... valeur ... } Exercices --------- .. quiz:: basesharp-arrayqcm :title: Plateforme .net Pour chacune de ces affirmations, indiquez si elle est vraie ou fausse: 1) :quiz:`{"type":"TF","answer":"T"}` Les cases d'un tableau sont initialisées par défaut lors de la création du tableau. #) :quiz:`{"type":"TF","answer":"F"}` Une variable de type tableau qui n'a pas été initialisée contient par défaut un tableau de taille 0. #) :quiz:`{"type":"TF","answer":"F"}` Si je passe un tableau en paramètre d'une fonction, le contenu du tableau est copié dans la fonction appelée. #) :quiz:`{"type":"TF","answer":"T"}` Je peux créer un tableau dont la taille est donnée par une variable. .. quiz:: basecsharp-array1 :title: Déroulement de code Soit la fonction: .. code-block:: csharp :linenos: int F1() { int[] t = {4, 5, 2}; t[2] += t[1] + 4; int a = 1; int b = t[a]; return t[0] + b + t[2]; } La fonction retourne la valeur: :quiz:`{"type":"FB","answer":"20"}` .. quiz:: basecsharp-array2 :title: Déroulement de code Soit la fonction: .. code-block:: csharp :linenos: int F2() { int[] t = {4, 1, 3}; int[] s = {2, 1, 0}; int a = t[s[1]]; int b = t[s[0]]; int c = t[s[s[2]]]; return a + b + c; } La fonction retourne la valeur: :quiz:`{"type":"FB","answer":"7"}` .. quiz:: basecsharp-array4 :title: Déroulement de code Soit la fonction: .. code-block:: csharp :linenos: void F4() { int[,] t = new int[3,3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) t[i,j] = i + j + j * i; } Le contenu du tableau ``t`` à la fin de la fonction ``F4`` est :quiz:`{"type":"FB","answer":"{{0, 1, 2}, {1, 3, 5}, {2, 5, 8}}","flags":"nospace"}` (utilisez le format standard pour l'initialisation des tableaux ``{ {....}, {....}}``) .. quiz:: basecsharp-arrayref :title: Tableaux et type référence Soit le code: .. code-block:: csharp :linenos: void Gi(int i) { i = 2; } void Hi(int i) { i++; } void Gt(int[] t){ t = new int[2]; t[0] = 1; } void Ht(int[] t){ t[0]++; } void Test() { int i1 = 0; int i2 = 0; int [] t1 = {0, 0}; int [] t2 = {0, 0}; Gi(i1); Hi(i2); Gt(t1); Ht(t2); } La valeur de ``i1`` à la fin de la fonction ``Test`` est :quiz:`{"type":"FB","answer":"0"}` La valeur de ``i2`` à la fin de la fonction ``Test`` est :quiz:`{"type":"FB","answer":"0"}` La valeur de ``t1`` à la fin de la fonction ``Test`` est :quiz:`{"type":"FB","answer":"{0, 0}","flags":"nospace"}` (en utilisant le format standard ``{....}``) La valeur de ``t2`` à la fin de la fonction ``Test`` est :quiz:`{"type":"FB","answer":"{1, 0}","flags":"nospace"}` (en utilisant le format standard ``{....}``) Chaines de caractères ===================== Les chaines de caractères sont désignées par le type ``String`` (ou ``string`` avec une minuscule) qui est de **type référence**. Les constantes de type chaine de caractères s'écrivent entre **double quotes** ``"``. .. code-block:: csharp string s1 = "Bonjour"; String s2 = "Bonjour"; String s3 = ""; // chaine de caractères vide L'**opérateur de concaténation** ``+`` permet de mettre 2 chaines de caractères bout à bout. Tous les types de données sont implicitement convertibles en chaines de caractères avec l'opérateur ``+``. Ainsi, une expression de la forme "chaine de caractères" + *n'importe quoi* est toujours valide et son résultat est une chaine de caractères. .. code-block:: csharp double d = 42.0; bool b = true; String s1 = "La valeur de d est " + d; // s1 = "La valeur de d est 42" String s2 = "" + b; // s2 = "true" Une chaine de caractères est composée de caractères au format unicode sur 2 octets (UTF16). Le backslash ``\`` sert de caractère d'échappement pour obtenir les caractères spéciaux : par exemple ``\n`` pour le retour à la ligne, ``\t`` pour une tabulation, ``\\`` pour obtenir un backslash. Le caractère ``@`` placée devant une constante de type ``String`` désactive l’interprétation des caractères spéciaux. Cela est particulièrement pratique pour les chemin d'accès aux fichiers : .. code-block:: csharp String filename = @"C:\dossier\fichier1.txt" ; L'API .net propose de nombreuses méthodes pour `manipuler les chaînes de caractères `_ : (``Replace``, ``SubString``, ``ToLower``, ``ToUpper``, ``Insert``...), test sur le contenu (``IsEmpty``, ``Compare``, ``Contains``, ``Equals``...), longueur de la chaine (``Length``), conversion vers les types de données primitifs (ex: ``ToInt32``). .. attention:: Les chaines de caractères sont **immutables**: on ne peut pas modifier une chaine après sa création. Toutes les opérations sur les chaînes de caractères entrainent la création d'une nouvelle chaine de caractères. Par exemple l'initialisation de la variable ``s`` dans le code ci-dessous : .. code-block:: csharp int i = 1; double d = 2; bool b = false; String s = " i = " + i + ", d = " + d + ", b = " + b + "."; // s = " i = 1, d = 2, b = false." implique un total de 13 chaines de caractères: * les 4 constantes entre double quote; * les 3 chaines issues des conversions implicites de ``i``, ``d`` et ``b``; * les 6 chaines créées par chacun des opérateurs ``+``. Exercices --------- .. quiz:: basecsharpqcmfonction1 :title: QCM les chaines de caractères 1) :quiz:`{"type":"TF","answer":"T"}` Le type ``String`` est de type référence. #) :quiz:`{"type":"TF","answer":"F"}` Pour modifier le 2ème caractère d'une chaine de caractère ``s``, je peux écrire ``s[1] = 'a';``. #) :quiz:`{"type":"TF","answer":"F"}` Pour stocker n caractères j'ai besoin de n octets. #) :quiz:`{"type":"TF","answer":"T"}` Tous les types de données peuvent être converti en chaine de caractères. .. quiz:: basecsharp-arraystringconcat :title: String immutable Soit la fonction: .. code-block:: csharp :linenos: public String StringifyArray(int [] a){ String r = "{"; for(int i = 0; i < a.Length-1; i++) { r = r + a[i] + ", "; } if(a.Length>0) { r = r + a[a.Length-1]; } r = r + "}"; return r; } Cette fonction appellée avec en paramètre le tableau d'entier contenant les valeurs 0, 1 et 2 retourne la chaine de caractère :quiz:`{"type":"FB","answer":"{0, 1, 2}"}` Cette fonction appellée avec en paramètre le tableau d'entier contenant les valeurs 0, 1 et 2 va crééer :quiz:`{"type":"FB","answer":"9"}` chaines de caractères (On ne comptera pas les chaines constantes données entre double quote, pensez aux conversions implicites et aux opérateurs ``+``). Cette fonction appellée avec en paramètre un tableau d'entier contenant n>0 éléments va créer :quiz:`{"type":"FB","answer":"3n"}` chaines de caractères (On ne comptera pas les chaines constantes données entre double quote). Exercices ========= .. quiz:: tpDebug :title: Debogage Visual Studio possède un débogeur intégré qui va vous aider dans ce genre de situation. Lorsque l'on compile un projet sous Visual Studio, on peut choisir le mode **Debug** ou **Release**. Le mode Release sert à compiler le programme avant sa diffusion à l'utilisateur: toutes les optimisations seront utilisées par le compilateur pour que le programme soit le plus rapide possible (quitte à réécrire des bouts de votre code afin de le rendre plus efficace). En mode Debug, le compilateur va au contraire produire un programme dont les instructions collent exactement au code que vous avez écrit afin de pouvoir suivre son exécution pas à pas. Normalement, durant la phase de développement on compile en mode Debug. Téléchargez la :download:`solution Visual Studio `, décompressez l'archive, ouvrez la solution, ouvrez le fichier *Program.cs* et laissez vous guider par les commentaires ! .. quiz:: arrays :title: Manipulation de tableaux Pour cet exercice, commencez par télécharger la :download:`solution Visual Studio `, décompressez l'archive et ouvrez la solution. Ce projet comporte des tests unitaires; commencez par mettre à jour les plugins Nugets de Visual Studio qui gèrent les tests. Allez dans le menu "Outils -> Gestionnaire de Package NuGet-> Gérer les packages NuGet..." .. image:: vsnuget1.png Clickez sur l'onglet "Mises à jour", sélectionnez tous les packages, puis cliquez sur le bouton "Mettre à jour". Acceptez les conditions et attendez la fin du processus de mise à jour. .. image:: vsnuget2.png .. important:: A chaque fois que vous écrivez une fonction, exécutez les tests unitaires (menu *Test -> Exécuter -> Tous les tests* ou touches ``Ctrl+r Ctrl+a``). Une fonction n'est pas correcte tant qu'il y a un test qui échoue sur la fonction. .. image:: test.png * Complétez la fonction ``InsererTableau`` recevant en paramètre deux tableaux d'entiers ``origine`` et ``aInserer`` ainsi qu'un indice ``position`` compris entre 0 et la taille du tableau ``origine``. Cette fonction retourne un nouveau tableau d'entiers correspondant au tableau ``origine`` où l'ensemble des valeurs de ``aInserer`` ont été insérées à l'élément d'indice ``position``. Exemple: .. code-block:: csharp int [] t1 = {4, 5, 3, 8, 9, 1, 2}; int [] t2 = {11, 15, 13}; int [] t3 = InsererTableau(t1, t2, 2); // t3 = {4, 5, 11, 15, 13, 3, 8, 9, 1, 2} * Complétez la fonction ``TransposerMatrice`` permettant d'effectuer la symétrie d'un tableau de nombres réels bidimensionnel carré de taille n: .. image:: transposeeMatrice.png :align: center * Complétez la fonction ``RemplirRectangle`` recevant en paramètre un tableau d'entiers bidimensionnel, 2 cordonnées entières ``x`` et ``y`` et 2 dimensions entières ``largeur`` et ``hauteur``. Cette fonction dessine un rectangle dans le tableau en écrivant des 1 dans les cases. Le coin supérieur gauche du rectangle est donné par les coordonnées ``(x,y)``, les dimensions du rectangle sont données par ``largeur`` et ``hauteur``. Attention: le rectangle peut sortir du tableau ! .. code-block:: csharp int [] t = new int[5,7]; RemplirRectangle(t, 3, 2, 5, 2); /* t = {{0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1}, {0, 0, 0, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0}} */ .. Complétez la fonction ``Remplissage1`` qui crée un tableau bidimentionnel d'entiers de taille ``dimension`` reçu en paramètre, avec ``dimension`` impair, de la manière suivante : .. image:: remplissage1.png :align: center .. .. quiz:: remplissageTableau3 :title: Triangle de Pascal Complétez la fonction ``TriangleDePascal`` recevant en paramètre une dimension ``n`` et qui retourne un tableau d'entiers bidimensionnel carré de dimension ``n`` initialisé comme un triangle de pascal: .. image:: pascal.png :align: center .. .. quiz:: contour :title: Contour Complétez la fonction ``ExtractionContour`` qui prend en entrée un tableau d'entiers bidimensionnel correspondant à une image noir et blanc. La valeur 0 correspond au fond blanc et la valeur 1 à la présence d'un objet. On veut appliquer un opérateur qui conserve dans l’image les cases noires ayant pour cases voisines au moins une case blanche. Par case voisine, nous désignons les quatre cases au dessus, en dessous, à gauche et à droite. Une fois la méthode appliquée, certaines cases noires sont devenues blanches. Voici un exemple du traitement : .. image:: contour.png :align: center .. .. raw:: html
		using static System.Console;
		// utilisez WriteLine(valeur); pour afficher un résulat

		//--------