Version en ligne

Tutoriel : Apprenez à programmer en C# sur .NET

Table des matières

Apprenez à programmer en C# sur .NET
Introduction
Préface
Comment sont créés les programmes ?
Le langage C# et le framework .NET
Installer un IDE pour programmer
Tour d'horizon de Visual C# Express 2010
Premiers pas sur console
Création de la solution
Lancement depuis l'invite de commandes (console)
Les variables
Qu'est-ce que c'est ?
Utiliser des variables
Type valeur ou référence ?
Les énumérations
Les opérateurs
Les opérateurs mathématiques
Les opérateurs logiques
Autres opérateurs
Les conditions et les boucles conditionnelles
if et sa famille
switch : un if spécial
while
for
Les méthodes
Créer une méthode
Appeler une méthode
La surcharge
Les classes (1/2)
Créer une classe
Créer un objet
Utiliser un objet
Les classes (2/2)
Le modificateur "static"
Application console : étude du code
Les tableaux
Tableaux simples
Tableaux à deux dimensions
Tableaux de tableaux
Effectuer des conversions
Conversions implicites
Conversions explicites (casts)
Classes et méthodes de conversion
Manipuler les chaînes de caractères
La concaténation
Les chaînes de formatage
La classe StringBuilder
Nouveautés sur la console et les opérateurs
Envoyer des arguments à un programme qui démarre
Interagir avec la console
Évaluations paresseuses des opérateurs && et ||
TP : Plus ou moins
Le jeu
Correction
Les WinForms (ou Windows Forms)
Création de la solution
Boîte à outils et contrôles
La classe MessageBox
Les évènements
Les gestionnaires d'évènements
Les paramètres passés
Travailler avec le temps
Les structures DateTime et TimeSpan
Les contrôles MonthCalendar et DateTimePicker
Les classes Timer
Gérer les exceptions
Lancer er intercepter des exceptions
Créer ses propres exceptions
Quelques exceptions courantes
Les interfaces
Créer une interface
Utiliser une interface existante
Utiliser plusieurs interfaces
Lire et écrire dans un fichier
Structure d'un fichier
Lire et écrire via des boîtes de dialogue
Les collections
Aperçu des génériques
Les listes
Les dictionnaires
Les piles
foreach ... in

Apprenez à programmer en C# sur .NET

Vous avez entendu parler du langage C, du C++, et voilà qu'on vous présente maintenant le C# !
Encore un langage me direz-vous ? Oui, mais pas n'importe lequel !

Il existe des centaines de langages de programmation, comme le C, le C++, Python, Java... Chacun a ses avantages et défauts. Le C# (aussi écrit C Sharp et prononcé "cé sharp" voire "ci sharp" à l'anglaise) n'est peut-être qu'un langage de plus à vos yeux, mais il est en fait très important dans le monde de la programmation !

Alors, qu'est-ce qui caractérise le C# ?

Bonne lecture, amusez-vous bien ! :)

Paint .NETPaint .NET

Windows Phone 7Windows Phone 7

Visual StudioVisual Studio

Paint .NET (programme C#), Windows Phone 7 et le code sous Visual Studio

Introduction

Préface

Dans ce tout premier chapitre, nous allons découvrir ce qu'est le C#, son histoire et son rapport avec le framework .NET. D'ailleurs, vous ne savez pas ce qu'est un framework ? Ce n'est pas grave, je vous rappelle que c'est un cours pour débutants, tout cela sera expliqué ! :)

Avant d'aller plus loin, une petite préface vous présentera l'esprit du tutoriel et vous donnera quelques conseils pour le suivre dans les meilleures conditions.

Préface

Introduction Comment sont créés les programmes ?

À qui s’adresse ce tutoriel ?

Aux débutants ! Pas besoin d'avoir fait de la programmation avant pour suivre ce tutoriel ! Je vais donc faire de mon mieux pour détailler au maximum mes explications, c'est promis. :)
Mon but est réellement de rendre ce tutoriel accessible pour les débutants.

Bien sûr, il y en a peut-être parmi vous qui ont déjà fait du C, du C++, du Java... Évidemment, si vous avez déjà programmé, ce sera plus facile pour vous (surtout pour la première partie qui présente les bases du langage). Attention néanmoins de ne pas vouloir aller trop vite : le C# ressemble à d'autres langages mais il a quand même ses spécificités !

Esprit du tutoriel

Nous allons découvrir ensemble de nombreuses choses en apprenant à programmer en C#. Il y aura bien entendu des TP pour vous faire pratiquer, afin que vous puissiez vous rendre compte de ce que vous êtes capables de faire après avoir lu plusieurs chapitres plus théoriques.

Néanmoins, je veux que vous soyez actifs ! Ne vous contentez pas de lire passivement mes explications, même lorsque les chapitres sont plutôt théoriques ! Testez les codes et les manipulations au fur et à mesure. Essayez les petites idées que vous avez pour améliorer ou adapter légèrement le code. Sortez un peu des sentiers battus du tutoriel : cela vous fera pratiquer et vous permettra de découvrir rapidement si vous avez compris ou non le chapitre.
Pas d'inquiétude, si jamais vous bloquez sur quoi que ce soit qui n'est pas expliqué dans ce cours, la communauté qui sillonne les forums saura vous apporter son aide précieuse. Au passage, je tiens à vous rappeler que la section "Commentaires" est exclusivement destinée aux commentaires et n'a pas pour vocation de regrouper les questions concernant ce tutoriel ; les forums sont là pour ça.

Durée d’apprentissage

Il faut compter plusieurs semaines, voire plusieurs mois, pour atteindre un niveau solide, vous permettant de commencer à faire des choses sérieuses plutôt que de "bricoler". D'ailleurs merci de m'informer du temps que vous a pris la lecture de ce cours pour que je puisse indiquer aux autres lecteurs une durée moyenne de lecture. ;)

Composition du cours

Un lecteur attentif aura remarqué que le titre est "Apprenez à programmer en C# sur .NET" (qui se prononce "Apprenez à programmer en cé sharpe sur dotte nette").

Alors, allez-vous apprendre le C# ou le .NET ? En fait, vous allez voir que l'un ne va pas sans l'autre :

Le tutoriel est loin d'être fini, vous ne voyez donc pas toutes les parties. Voici ce que j'envisage, pour l'instant, de traiter :

Le début de ce cours sera plutôt théorique. Pour savoir coder, il faut commencer par apprendre les bases du langage, c'est un passage obligé. ;)
Petit à petit j'introduirai la pratique pour illustrer certains points importants ; cela vous permettra de mieux comprendre des fonctionnements et surtout de bien mémoriser le cours.

Le code

Parce que c'est la norme chez la quasi-totalité des développeurs professionnels, le code source que nous allons voir sera écrit en anglais. On prend l'habitude d'écrire en anglais car le code peut ensuite facilement être relu par des personnes d'autres nationalités, anglophones ou non.
Imaginez qu'un espagnol souhaite corriger le code d'un allemand, et que le tout est relu par un français... Si chacun écrit dans sa langue natale, cela donne quelque chose de vraiment horrible (ça sent le vécu :-° ).

Rassurez-vous ! Ce n'est pas parce que, dans le code source, la "fenêtre" sera appelée "window" que ce cours sera plus difficile ! Toutes mes explications seront en français, et les commentaires dans le code pour vous guider seront en général en français.

J'ai fait le choix d'écrire le code source en anglais pour vous habituer à travailler avec du code anglais, car c'est ce que font les programmeurs dignes de ce nom (pas que chez les professionnels d'ailleurs). Pas besoin d'être un génie en anglais pour lire et écrire du code dans cette langue. ;)

En outre, je vous conseille de prendre les mêmes habitudes d'écriture que moi, aussi bien pour la mise en forme que pour la façon de nommer les choses. Pourquoi ? Parce que coder c'est bien, mais coder proprement, c'est mieux. En fait, il s'agit de conventions de codage respectées par "ceux qui savent programmer". Respecter ces conventions montre déjà que vous n'êtes pas le premier venu qui écrit n'importe quoi. Après assimilation de ce tutoriel, vous serez un vrai développeur qui a des habitudes de développeur. Il vous sera ainsi plus facile de communiquer avec d'autres personnes.

Allez plus loin !

N'hésitez pas à regarder d'autres tutoriels portant sur le sujet. Il est toujours bon de diversifier ses sources pour avoir différentes approches du sujet.

De manière générale, je vous recommande de ne pas hésiter à tester les codes que je présenterai au fur et à mesure. Surtout, si vous avez des idées pour les améliorer un peu, faites-le ! Ça ne marchera pas à tous les coups, mais cela vous fera beaucoup plus progresser que vous ne le pensez ! Ne comptez donc pas uniquement sur les TP pour pratiquer, ce serait vraiment dommage.

Sachez aussi que je suis ouvert à toutes remarques, critiques, questions, insultes... portant sur ce tutoriel. N'hésitez donc pas à poster des commentaires, surtout si votre message peut être utile pour d'autres personnes. Par contre, veuillez ne pas m'envoyer de MP, sauf en cas de force majeure, parce que je n'aurai pas le temps de vous répondre individuellement (la rédaction de ce tutoriel en elle-même nécessite déjà beaucoup de temps), et que s'il s'agit d'une demande d'aide, les forums sont là pour ça et on vous y répondra plus rapidement que moi.


Introduction Comment sont créés les programmes ?

Comment sont créés les programmes ?

Préface Le langage C# et le framework .NET

Un programme : qu'est-ce que c'est ?

Comme vous le savez, votre ordinateur exécute des programmes pour effectuer des tâches. Vous utilisez des programmes tous les jours :

Ce sont tous des programmes !

Votre ordinateur ne peut exécuter ces programmes que s'ils sont écrits dans le seul langage qu'il comprend, le binaire :

Lecture de code binaire

L'ordinateur lit les programmes en langage binaire.

Le processeur de votre ordinateur lit et comprend ce code, qui lui indique ce qu'il doit faire.

Mais... on ne va pas écrire des programmes en binaire quand même ?! C'est trop compliqué !

Bien sûr que non. Personne n'écrit sérieusement des programmes directement en binaire. C'est, comme vous le dites, trop compliqué. Voilà pourquoi on écrit les programmes dans des langages plus simples et plus "humains".

Comment créer des programmes "simplement" ?

Il existe des centaines de langages de programmation dont le but est de simplifier l'écriture de ce code binaire/assembleur. En effet, les langages que l'on utilise traditionnellement pour programmer, comme le C et le C++, nous permettent de créer des programmes bien plus facilement qu'en assembleur (binaire).

Je vais vous expliquer rapidement le principe de fonctionnement des langages "traditionnels" comme le C et le C++, puis je vous présenterai le fonctionnement du C#. Comme le C# est plus récent, on a pu améliorer son fonctionnement par rapport au C et au C++ comme nous allons le voir.

Langages traditionnels : la compilation

Avec des langages traditionnels comme le C et le C++, on écrit des instructions simplifiées, lisibles par un humain comme :

printf("Bonjour");

Ce n'est pas vraiment du français, mais c'est quand même beaucoup plus simple que le binaire ou l'assembleur vous avouerez ! :D

Bien entendu, l'ordinateur ne comprend pas ces instructions. Lui, il veut du binaire, du vrai. :pirate:
Pour obtenir du binaire à partir d'un code écrit en C ou C++, on doit effectuer ce qu'on appelle une compilation. C'est un programme qui traduit le code source en binaire exécutable :

Compilation d'un programme

La compilation permet de traduire le code source en binaire.

Cette méthode est efficace et a fait ses preuves. De nombreuses personnes développent toujours en C et C++ aujourd'hui. Néanmoins, ces langages ont aussi un certain nombre défauts dus à leur ancienneté. Par exemple, un programme compilé (binaire) ne fonctionne que sur la plateforme sur laquelle il a été compilé. Cela veut dire que si vous compilez sous Windows, vous obtenez un programme qui fonctionne sous Windows uniquement (et sur un type de processeur particulier). Impossible de le faire tourner sous Mac OS X ou Linux simplement, à moins de le recompiler sous ces systèmes d'exploitation (et d'effectuer au passage quelques modifications).

Compilation sous différentes plateformes

Il faut compiler pour chaque environnement (système d'exploitation) différent.

Les programmes binaires ont ce défaut : ils ne fonctionnent que pour un seul type de machine. Pour les développeurs qui écrivent le code, c'est assez fastidieux à gérer.

Langages récents : le code managé

Les langages récents, comme le C# et le Java, résolvent ce problème de compatibilité tout en ajoutant de nombreuses fonctionnalités appréciables au langage, ce qui permet de réaliser des programmes beaucoup plus efficacement. :)

La compilation en C# ne donne pas un programme binaire, contrairement au C et au C++. Le code C# est en fait transformé dans un langage intermédiaire (appelé CIL, anciennement MSIL) que l'on peut ensuite distribuer à tout le monde. Ce code, bien sûr, n'est pas exécutable lui-même, car l'ordinateur ne comprend que le binaire.

Regardez bien ce schéma pour comprendre comment cela fonctionne :

Compilation du C#

Traitement du code pour en faire un programme exécutable.

Le code en langage intermédiaire (CIL) correspond au programme que vous allez distribuer. Sous Windows, il prend l'apparence d'un .exe comme les programmes habituels, mais il ne contient en revanche pas de binaire.

Lorsqu'on exécute le programme CIL, celui-ci est lu par un autre programme (une machine à analyser les programmes, appelée CLR) qui le compile cette fois en vrai programme binaire. Cette fois, le programme peut s'exécuter, ouf ! :D

Ça complique bien les choses quand même ! Est-ce bien utile ?

Cela offre beaucoup de souplesse au programmeur. Le code en langage intermédiaire (CIL) peut être distribué à tout le monde. Il suffit d'avoir installé la machine CLR sur son ordinateur, qui peut alors lire les programmes en C# et les compiler "à la volée" en binaire. Avantage : le programme est toujours adapté à l'ordinateur sur lequel il tourne.

Cette complexité ralentit légèrement la vitesse d'exécution des programmes (par rapport au C ou au C++), mais la différence est aujourd'hui vraiment négligeable par rapport aux gains que cela apporte. On parle de code managé.


Préface Le langage C# et le framework .NET

Le langage C# et le framework .NET

Comment sont créés les programmes ? Installer un IDE pour programmer

Qu'est-ce que le langage C# ? Qui l'a créé ? Quel est le rapport avec .NET ? Et puis, qu'est-ce que c'est .NET ?

Autant de questions auxquelles nous allons répondre maintenant. :)

Le C# ressemble beaucoup au Java dont il est inspiré, bien que les deux langages présentent des différences notables. Il reprend aussi certaines notions du C/C++ mais s'en démarque sensiblement.

Seul, il ne sert pas à grand chose. En effet, le langage a besoin d'une boîte à outils pour exécuter facilement des actions courantes, comme ouvrir un fichier, communiquer sur le réseau, ouvrir une fenêtre, réduire une fenêtre, accéder à une base de données, etc.

Cette boîte à outils, que l'on utilise quasi-systématiquement en C#, c'est le framework .NET. C'est un peu comme un énorme couteau suisse qui offre toutes les possibilités dont vous pouvez rêver. ;)

Ce framework .NET n'est pas forcément lié au langage C#. Il est aussi possible de l'utiliser dans d'autres langages comme Visual Basic (plus simple, pour les programmeurs occasionnels), J# (plus proche de Java pour les habitués de Java) ou encore F# (langage fonctionnel).

Framework .NET et langages

Quelques langages permettant d'utiliser le framework .NET.

Pile de composants .NET

Les différentes couches du framework .NET.
Image 010.010.20100

On dit que .NET n'est pas une technologie mais un ensemble de technologies (c'est vous dire si c'est énorme en termes de possibilités !). Je me doute bien que "ensemble de technologies" ne vous dit pas concrètement ce qu'est .NET alors voici ces technologies ci-contre (cf. image 010.010.20100).

Tous ces mots techniques ne doivent pas vous effrayer, c'est normal de ne rien y comprendre pour le moment. ^^

En revanche, il est intéressant de comprendre que .NET est divisé en plusieurs blocs (on parle de bibliothèques) qui offrent chacun des services différents.

Vous voyez en particulier les WinForms qui sont les fenêtres graphiques d'un programme, ADO.NET qui permet d'utiliser des bases de données, et WPF qui est une franche amélioration des WinForms (notamment sur le plan du design).

Aperçu historique

Fondateur du C# et de .NET

Anders Hejlsberg.
Image 010.010.20000

C'est Microsoft qui est à l'origine du projet .NET et de son langage C#. En 2001, Anders Hejlsberg (le créateur du langage Delphi), employé chez Microsoft, crée le framework .NET (prononcé comme "dotte nette") et son langage phare : le C# (écrit C Sharp et prononcé comme "cé charpe"). Les deux sont complémentaires, comme nous allons le voir plus loin.

En 2002, la première version du .NET voit le jour et l'environnement de développement de Microsoft, Visual Studio, passe à la version 7.0 pour intégrer le .NET. S'ensuivent plusieurs versions importantes du framework .NET : 2.0, 3.0, 3.5, 4.0.

Actuellement, la dernière version du framework .NET est la 4.0, parue en avril 2010. Pour accompagner cette sortie, l'environnement de développement (le logiciel qui permet de programmer) a lui aussi évolué. Pour travailler en C#, .NET on utilise désormais Visual Studio 2010 (version 10.0). Les anciennes versions fonctionnent toujours mais il est recommandé de se mettre à jour.

Exemple de programme : Paint .NET

Paint .NET est un éditeur d'images gratuit entièrement écrit à l'aide du framework .NET, dont voici un screenshot :

404 Image not found

Paint .NET, éditeur d'images gratuit entièrement écrit à l'aide du framework .NET.
Image 010.010.20200

Vous pouvez d'ailleurs le télécharger sur le site de Paint .NET

À quoi ressemble le C# ?

Voici un code minimal qui affiche "Hello World!" :

using System;
 
class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World!");
    }
}

Nous aurons l'occasion de comprendre ce que tout cela veut dire plus loin. ;)

Voici un code un peu plus long et complet qui gère (en partie) l'affichage d'une fenêtre, pour vous faire une idée. Je ne vous donne ce bout de code qu'à titre illustratif là encore :

#region File Description
//-----------------------------------------------------------------------------
// MainForm.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#endregion

namespace WinFormsContentLoading
{
    /// <summary>
    /// Custom form provides the main user interface for the program.
    /// In this sample we used the designer to fill the entire form with a
    /// ModelViewerControl, except for the menu bar which provides the
    /// "File / Open..." option.
    /// </summary>
    public partial class MainForm : Form
    {
        ContentBuilder contentBuilder;
        ContentManager contentManager;


        /// <summary>
        /// Constructs the main form.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            contentBuilder = new ContentBuilder();

            contentManager = new ContentManager(modelViewerControl.Services,
                                                contentBuilder.OutputDirectory);

            /// Automatically bring up the "Load Model" dialog when we are first shown.
            this.Shown += OpenMenuClicked;
        }


        /// <summary>
        /// Event handler for the Exit menu option.
        /// </summary>
        void ExitMenuClicked(object sender, EventArgs e)
        {
            Close();
        }


        /// <summary>
        /// Event handler for the Open menu option.
        /// </summary>
        void OpenMenuClicked(object sender, EventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();

            // Default to the directory which contains our content files.
            string assemblyLocation = Assembly.GetExecutingAssembly().Location;
            string relativePath = Path.Combine(assemblyLocation, "../../../../Content");
            string contentPath = Path.GetFullPath(relativePath);

            fileDialog.InitialDirectory = contentPath;

            fileDialog.Title = "Load Model";

            fileDialog.Filter = "Model Files (*.fbx;*.x)|*.fbx;*.x|" +
                                "FBX Files (*.fbx)|*.fbx|" +
                                "X Files (*.x)|*.x|" +
                                "All Files (*.*)|*.*";

            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                LoadModel(fileDialog.FileName);
            }
        }


        /// <summary>
        /// Loads a new 3D model file into the ModelViewerControl.
        /// </summary>
        void LoadModel(string fileName)
        {
            Cursor = Cursors.WaitCursor;

            // Unload any existing model.
            modelViewerControl.Model = null;
            contentManager.Unload();

            // Tell the ContentBuilder what to build.
            contentBuilder.Clear();
            contentBuilder.Add(fileName, "Model", null, "ModelProcessor");

            // Build this new model data.
            string buildError = contentBuilder.Build();

            if (string.IsNullOrEmpty(buildError))
            {
                // If the build succeeded, use the ContentManager to
                // load the temporary .xnb file that we just created.
                modelViewerControl.Model = contentManager.Load<Model>("Model");
            }
            else
            {
                // If the build failed, display an error message.
                MessageBox.Show(buildError, "Error");
            }

            Cursor = Cursors.Arrow;
        }
    }
}

Comment sont créés les programmes ? Installer un IDE pour programmer

Installer un IDE pour programmer

Le langage C# et le framework .NET Tour d'horizon de Visual C# Express 2010

Un IDE ("Environnement de Développement Intégré", en français) est un éditeur de texte qui propose toute une panoplie d'outils pour développer des logiciels. S'il est en théorie possible de programmer avec n'importe quel éditeur de texte, les IDE nous simplifient beaucoup la vie en colorant le code et en offrant des outils pour automatiser certaines tâches répétitives. Malheureusement, ils n'écrivent pas encore le code à notre place ! ;)

Lequel choisir ?

Image utilisateur

Logo de Visual Studio 2010 Express.
Image 010.010.30000

Nous allons utiliser l'IDE associé au .NET, à savoir Visual Studio. Il ne fonctionne que sous Windows. Nous n'avons pas nécessairement besoin de la version complète, qui de surcroît est payante. Nous utiliserons la version gratuite qui est largement suffisante pour nous : Visual Studio Express.

Image utilisateur

Logo de Mono.
Image 010.010.30100

Il existe une implémentation libre du .NET qui regroupe une grande partie de ses technologies : Mono. Il permet de faire tourner les programmes .NET sous Linux. Mono dispose même d'un IDE libre, MonoDevelop, qui permet de coder en C# sous Windows, Mac OS et Linux.

Dans ce cours, nous utiliserons uniquement Visual Studio car la majorité des gens disposent de Windows et qu'à la base c'est l'IDE fait pour le .NET (il offre donc la possibilité de travailler avec toutes les technologies de cette plate-forme).

Image utilisateur

Logo de SharpDevelop.
Image 010.010.30101

Une autre alternative à Visual Studio est SharpDevelop, un IDE Open Source conçu pour développer du .NET.
Par contre, il n'est disponible que pour Windows, au regret des utilisateurs de Mac OS X et de Linux.

Installation de Microsoft Visual C# 2010 Express Edition

Vous pouvez télécharger Visual Studio sur le site de Microsoft :

Télécharger l'ISO de Visual Studio Express 2010

Si le lien n'est pas valide, allez sur cette page, cliquez sur "All - Offline Install ISO image file",
en veillant à bien sélectionner "English" pour la langue.

Cet ISO contient Microsoft Visual C# 2010 Express. Vous aurez besoin d'un émulateur pour accéder au contenu de l'iso. DAEMON Tools Lite fait l'affaire ; si vous n'avez pas d'émulateur, installez-le et lancez-le.
Avant toute chose, faites un clic-droit sur l'icône de DAEMON Tools Lite dans la barre des tâches et cliquez sur "Préférences":

404 Image not found

Options de DAEMON Tools Lite dans la barre des tâches.
Image 010.010.30200

Cochez la case "Auto montage" (cf. image 010.010.30300) ; cela permet de remonter automatiquement les images montées avant l'arrêt de votre ordinateur. Étant donné que l'installation de Visual Studio requiert un redémarrage, sans cette option une erreur se produira au redémarrage : l'installeur ne pourra plus accéder au disque virtuel.

404 Image not found

Préférences de DAEMON Tools Lite.
Image 010.010.30300

Montez une image avec l'ISO, ouvrez l'Explorateur Windows et lancez l'installeur du disque virtuel de Visual Studio. Sélectionnez Visual C# :

404 Image not found

Cliquez sur Visual C# 2010 Express dans l'installeur.
Image 010.010.30400

Avancez dans l'installation jusqu'à voir l'image 010.010.30500. Cochez la case pour SQL Server 2008. Il se peut que vous ayez aussi une case pour Silverlight ; si c'est le cas laissez-la cochée aussi (case absente sur l'image car sur mon PC Silverlight est déjà installé).

404 Image not found

Installeur de Visual Studio 2010 Express.
Image 010.010.30500

Lorsque l'installation commence, vous devriez avoir :

404 Image not found

Progression de l'installation.
Image 010.010.30600

Au bout d'un certain temps (pour ne pas dire un temps certain), l'installeur va vous demander de redémarrer votre PC :

404 Image not found

Redémarrage requis pour continuer l'installation.
Image 010.010.30700

Une fois l'installation terminée, vous devez voir :

404 Image not found

Installation achevée avec succès.
Image 010.010.30800

Cliquez sur "Help" > "Register Product" :

404 Image not found

Enregistrement du produit.
Image 010.010.30900

Cliquez sur "Obtain a register key online" pour enregistrer votre produit :

404 Image not found

Obtention d'une clef d'activation.
Image 010.010.31000

Cela va ouvrir une page web. Il faut que vous vous connectiez avec une Windows Live ID (adresse e-mail chez un service Microsoft ; ex: [email protected] ou [email protected] ou encore [email protected] devraient faire l'affaire). Vous devez à présent vous farcir tout le formulaire d'enregistrement. :p Vous y êtes malheureusement obligés si vous voulez pouvoir utiliser Visual C# Express autant que vous voudrez.

Vous allez recevoir votre clé d'enregistrement par e-mail. Dans certains e-mails, on vous donne aussi une clé pour essayer Visual Studio Pro, mais nous n'en avons pas besoin.
Dans Visual Studio, cliquez à nouveau sur "Help" > "Register Product". Vous voyez la même fenêtre que tout à l'heure.
Entrez votre clé et cliquez sur "Register Now". Si tout s'est passé correctement, vous voyez ça :

404 Image not found

Enregistrement déjà réalisé.
Image 010.010.31100

Et voilà, l'installation est finie. Maintenant, faisons un petit tour d'horizon de votre nouvel IDE.


Le langage C# et le framework .NET Tour d'horizon de Visual C# Express 2010

Tour d'horizon de Visual C# Express 2010

Installer un IDE pour programmer Premiers pas sur console

Allure générale

Ce n'est pas très compliqué d'apprendre à se servir de Visual Studio alors je vais vous dire les grandes lignes. ;)
Vous devez avoir quelque chose qui ressemble à ça :

404 Image not found

Les principaux éléments de Visual Studio.
Image 010.010.40000

Fonctionnement

Le code d'un programme est placé dans des fichiers. Ces fichiers sont rangés dans des projets qui eux-mêmes font partie d'une solution. La solution est le plus grand "conteneur".

Bonus

Une autre raison pour laquelle j'ai choisi de travailler avec Visual Studio est que cet IDE fournit des fonctionnalités très intéressantes puisqu'elles nous simplifient le travail.

Le compilateur

Le compilateur vérifie la syntaxe de votre code. Si votre code est écrit correctement, le compilateur le convertit pour qu'il puisse par la suite être exécuté. Tant que votre programme n'est pas converti en CIL, il ne peut pas être utilisé.
Vous pourriez avoir un autre compilateur, mais étant donné que Visual Studio en intègre déjà un, ne nous compliquons pas la vie !
Pour compiler votre solution, cliquez sur "Debug" > "Build Solution" (F6 sur les nouvelles versions et Ctrl + Shift + B sur les anciennes).

Le debugger

J'imagine que vous savez ce qu'est un bug : c'est une erreur dans un programme. Faire du debug correspond donc à traquer ces erreurs. Pour cela, on utilise un debugger ; il permet de lancer votre programme en mode test. C'est indispensable pour dénicher la plupart des bugs. En cas d'erreur, vous êtes avertis et on vous montre l'erreur, ce n'est pas comme quand une application crashe et qu'on doit soudainement fermer le programme.
Pour ce faire, cliquez sur "Debug" > "Start Debugging" (F5).
Pour afficher la barre d'outils pour le debug, cliquez sur "View" > "Toolbars" et assurez vous que "Debug" est sélectionné.
Normalement, vous avez dans votre barre d'outils des boutons semblables à des boutons de lecture :

404 Image not found

Barre d'outils relative au mode debug.
Image 010.010.40100

Dans l'ordre, de gauche à droite :

Dans votre code, vous pouvez mettre des points d'arrêt (breakpoints) sur une ligne. Lorsque cette ligne s'apprête à être exécutée, le programme passe en pause et dans Visual Studio vous pouvez voir la valeur de vos variables.
Pour mettre un point d'arrêt, appuyez sur F9 quand le curseur est sur une ligne. Vous pouvez aussi cliquer à gauche d'une ligne : à gauche de son numéro et dans la partie légèrement plus grise. Par défaut, les numéros de lignes ne sont pas affichés. Pour les afficher, allez dans "Tools" > "Options", cochez la case "Show all settings" tout en bas à gauche de la nouvelle fenêtre, ouvrez ensuite le nœud "Text Editor", cliquez sur "All languages" et cochez la case "Line numbers" dans la section "Display" à droite :

404 Image not found

Options de Visual Studio.
Image 010.010.40101

Voici à quoi ressemble un point d'arrêt en mode normal :

404 Image not found

Allure d'un point d'arrêt quand vous écrivez du code.
Image 010.010.40200

En mode debug, on obtient ceci :

404 Image not found

Le point d'arrêt bloque l'exécution avant la ligne.
Image 010.010.40300

La petite infobulle en-dessous indique que la variable myString vaut null. Elle ne vaut pas encore "Hello world!" car le point d'arrêt met en pause le programme avant l'exécution de la ligne.

L'IntelliSense

Lorsque vous commencez à écrire quelque chose, Visual Studio vous propose automatiquement la fin de votre mot grâce à l'IntelliSense.
Pour déclencher l'IntelliSense manuellement (si ça ne se fait pas déjà tout seul), appuyez sur Ctrl + Espace. Exemple :

404 Image not found

L'IntelliSense à l’œuvre.
Image 010.010.40400

Que diriez-vous d'entrer dans le vif du sujet ? On attaque dès le prochain chapitre !


Installer un IDE pour programmer Premiers pas sur console

Premiers pas sur console

Tour d'horizon de Visual C# Express 2010 Création de la solution

La console, aussi appelée "invite de commandes" ou "shell" est une fenêtre au fond noir (en général) dans laquelle on entre sous forme de texte des commandes à exécuter (ce programme est situé ici : C:\Windows\System32\cmd.exe). Ça n'a donc rien à voir avec une console de salon comme la Xbox 360 ou la PS3 (à l'oreillette on me dit que la Wii est une console ; ah bon ? :p).

La console va nous permettre d'afficher des chaînes de caractères. Nous allons commencer avec elle pour avoir un rendu visuel de ce que fait le code.

Création de la solution

Premiers pas sur console Lancement depuis l'invite de commandes (console)

Comme dit précédemment, la solution est ce qui va contenir notre programme. Une solution est faite de projets, qui contiennent chacun des fichiers de code, et des références pour charger les assemblies. Pour faire simple, une assembly contient du code déjà écrit et peut donc être appelée pour offrir ses services.
Nous allons créer une nouvelle solution ; celle-ci contiendra déjà un projet et des références vers les assemblies de base (en l'occurrence de quoi utiliser la console).

D'abord, lancez Visual Studio et cliquez dans la barre des menus sur "File" > "New Project". Vous devriez avoir ça :

404 Image not found

Les différents modèles que vous pouvez utiliser.
Image 020.010.10000

Cliquez sur "Cancel" pour revenir à la fenêtre principale.
Allez dans "Tools" > "Options". La fenêtre d'options s'affiche.
Cochez la case "Show all settings" en bas à gauche de cette fenêtre.
Allez dans "Projects and Solutions".
Sélectionnez "General".
Vous devriez avoir ça (votre nom d'utilisateur est à la place de "Narvarth") :

404 Image not found

Options de Visual Studio.
Image 020.010.10100

Cochez toutes les cases à droite et cliquez sur OK.
Refaites "File" > "New Project".
Magie ! :magicien: Après avoir sélectionné "Console Application", vous devez voir :

404 Image not found

Cette fois, on dispose de plus d'options.
Image 020.010.10200

Vous obtenez plus de détails maintenant :

Donnez un nom à votre solution et à votre projet et cliquez sur OK quand vous êtes prêts. Voici maintenant ce que vous voyez :

404 Image not found

Code déjà contenu dans le modèle d'un projet console.
Image 020.010.10300

Vous voyez l'arborescence de votre solution dans l'explorateur de solution.
À gauche vous voyez le code qui se trouve dans le fichier Program.cs (l'extension "cs" venant de "C Sharp"). Il existe d'autres extensions, notamment "resx" pour les fichiers de ressources. Nous verrons cela en temps utile.

Pour tester du code, mettez-le entre les deux accolades du bloc Main (sauf mention contraire de ma part).

Nous allons utiliser une méthode déjà créée qui va afficher du texte dans la console, c'est WriteLine. Cette méthode se situe dans la classe Console, donc pour y accéder il faut écrire Console.WriteLine. Vous aurez plus d'explications quand nous verrons les classes et vous pourrez ainsi mieux comprendre. ;) Pour l'instant, regardez comment ça fonctionne dans l'exemple suivant. Console.WriteLine("Hello World!"); affiche Hello World! en console, donc pour tester, écrivez :

using System;

namespace MyFirstApp
{
	class Program
	{
		static void Main(string[] args)
		{
			Console.WriteLine("Hello World!");
		}
	}
}

Pour fermer la console et terminer le programme, appuyez sur une touche ou fermez directement la fenêtre de la console.


Premiers pas sur console Lancement depuis l'invite de commandes (console)

Lancement depuis l'invite de commandes (console)

Création de la solution Les variables

Pour générer votre application (pour qu'elle soit compilée et convertie en .exe), cliquez sur "Debug" > "Build Solution" (F6 sous les nouvelles versions de Visual Studio, Ctrl + Shift + B sous les anciennes). Le volet "Output" s'ouvre et vous devez voir quelque chose comme :

404 Image not found

Notez le changement dans la fenêtre "Output".
Image 020.010.30000

"1 succeded" m'indique qu'un projet a bien été compilé. Notez que vous êtes aussi informés si une erreur survient.

Ouvrez l'explorateur de fichiers et allez dans le dossier de votre solution.
Allez ensuite dans les dossiers "bin" puis "Debug".
Vous trouverez un fichier .exe portant le nom de votre solution.
Lancez cet exécutable ; vous voyez apparaître et disparaître en un clin d'œil la console. Normal, pour le moment votre programme ne fait rien, alors sitôt ouvert il a fini et se ferme.

Je vais maintenant vous apprendre à lancer cet exécutable, non pas depuis l'explorateur, mais depuis la console. La console doit se trouver là : C:\Windows\System32\cmd.exe. Je vous conseille d'en faire un raccourci sur votre bureau car nous allons en avoir besoin assez souvent. Lancez cet exécutable (qui est l'invite de commandes), et vous voyez :

404 Image not found

La console Windows.
Image 020.010.30100

Copiez l'adresse de votre application jusqu'au dossier "Debug". Pour moi c'est "C:\Users\Narvarth\Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication1\bin\Debug".
Écrivez cd, laissez un espace, et collez l'adresse.

Faites un clic droit puis cliquez sur coller.

Appuyez sur Entrée pour effectuer la commande.

"cd" signifie "change directory" (changer de dossier) : on se téléporte dans le dossier où se trouve notre programme.

Tapez ensuite le nom de votre solution suivi de ".exe" et appuyez sur Entrée. Il ne va rien se passer car le programme ne fait rien. Voici ce que ça donne chez moi :

404 Image not found

Lancement du programme via la console.
Image 020.010.30200

Voilà, maintenant que vous savez un peu travailler avec la console, vous allez pouvoir tester le code que je vous donne.


Création de la solution Les variables

Les variables

Lancement depuis l'invite de commandes (console) Qu'est-ce que c'est ?

Une des questions qu'un débutant peut se poser est :

Que fait un programmeur quand il écrit du code ?

En fait, un programmeur manipule des variables.

Qu'est-ce que c'est ?

Les variables Utiliser des variables

Comme son nom l'indique, une variable est quelque chose qui varie. Sa représentation la plus courante est une boîte qui contient des informations.

Les deux éléments essentiels d'une variable sont sa valeur (et donc son type) et son nom. Nous parlerons des types juste après.
Le nom, c'est vous qui le fixez ; il est unique et permet de n'identifier qu'une seule variable. La valeur, quant à elle, peut être à peu près n'importe quoi, et peut être modifiée autant que l'on veut. Pour schématiser, la variable est la boîte, son nom est écrit dessus, sa valeur est ce qu'elle contient, et son type est la forme de la boîte.

Donner un nom à ses variables

En C#,

Types de variables de base

En C#, les variables possèdent un type qui précise la nature des informations stockées.
L'inconvénient que remarquent surtout ceux qui ont l'habitude d'un langage sans typage (tel que le PHP) est le manque de "souplesse" : certaines choses peuvent être écrites plus simplement dans ce langage qu'en C#. Par exemple, le chiffre 2 et sa représentation en caractère '2' peuvent éventuellement être utilisés l'un à la place de l'autre, ce qui n'est pas le cas en C#.
L'avantage est que vous savez exactement ce que vous manipulez. Cela permet d'éviter certaines erreurs, de bien comprendre ce que votre code fait, et de pouvoir l'optimiser.
C'est une habitude à prendre ; ensuite vous redouterez le "laxisme" de certains langages !

Le C# dispose de types de base qui permettent de représenter des informations dont la nature est courante, comme des nombres, des caractères et des chaînes de caractères. Il existe un nombre infini de types étant donné que l'on peut construire un nouveau type à partir de ceux qui existent déjà. C'est ce que nous verrons quand nous étudierons les classes.

Voici un tableau exhaustif contenant les types de base du C# :

Type C#

Type .NET

Signification

Taille en mémoire (en octets)

Domaine de valeurs

char

Char

character (caractère)

2

caractère Unicode (UTF-16) allant de U+0000 à U+ffff

string

String

chaîne de caractères

variable

référence sur une séquence de caractères Unicode

int

Int32

integer (nombre entier)

4

[-231; 231-1]

uint

UInt32

unsigned integer (nombre entier non signé)

4

[0; 232-1]

long

Int64

nombre entier long

8

[-263; 263-1]

ulong

UInt64

unsigned long (nombre entier long non signé)

8

[0; 264-1]

sbyte

SByte

signed byte (octet signé)

1

[-27; 27-1]

byte

Byte

octet

1

[0; 28-1]

short

Int16

nombre entier court

2

[-215; 215-1]

ushort

UInt16

unsigned short (nombre entier court non signé)

2

[0; 216-1]

float

Single

flottant (nombre réel)

4

±1,5*10-45 à ±3,4*10+38 (7 chiffres de précision)

double

Double

double flottant (nombre réel)

8

±5,0*10-324 à ±1,7*10+308 (15 à 16 chiffres de précision)

decimal

Decimal

nombre décimal

16

±1,0*10-28 à ±7,9*1028 (28 à 29 chiffres significatifs)

bool

Boolean

booléen

1

true / false

object

Object

référence d'objet

variable

référence d'objet

Chacun des types C# présentés est un alias du type .NET associé, cela signifie que lorsque que vous écrivez par exemple int, c'est comme si vous aviez écrit System.Int32. Pour raccourcir et par habitude, on privilégie le type C# au type .NET.

Illustration avec sizeof et typeof

sizeof permet de connaître la taille (en octets) occupée en mémoire par une variable de type valeur. Nous verrons très bientôt ce que cela veut dire ; pour faire simple, il faut que la taille soit fixe. Par exemple, on ne peut pas écrire sizeof(string), car une chaîne de caractères peut être de la taille que l'on veut. D'autre part, multiplier le nombre de caractères par la taille d'un seul caractère ne suffit pas non-plus ; en effet l'objet string contient en mémoire plus d'informations qu'une simple chaîne de caractères (cf. ce site).

typeof permet de connaître le type d'une variable.

Par exemple, le code suivant...

int i = 10;
string s = "Hello world!";
char c = 'a';

Console.WriteLine("i est de type " + typeof(int) + " et occupe " + sizeof(int) + " octet(s) en mémoire.");
Console.WriteLine("s est de type " + typeof(string) + ".");
Console.WriteLine("c est de type " + typeof(char) + " et occupe " + sizeof(char) + " octet(s) en mémoire.");

...affichera :

i est de type System.Int32 et occupe 4 octet(s) en mémoire.
s est de type System.String.
c est de type System.Char et occupe 2 octet(s) en mémoire.

Conventions d'écriture pour le typage des données écrites "en dur" dans le code

Dans votre code, vous pouvez directement entrer des valeurs à affecter aux variables. La question qui se pose est : de quel type sont les données entrées ? En effet, par exemple le chiffre 2 doit-il être compris comme un int, un uint, un double, ... ? Pour pallier à ce problème, des conventions ont été établies.

Un nombre entier est considéré comme un int s'il tient dans la plage de valeurs d'un int (exemple : -123), et comme un long s'il ne tient pas dans la plage de valeurs d'un int mais dans celle d'un long (exemple : 5111222333).
Un nombre écrit avec un u à la fin symbolise un uint. Exemple : 2u (ou 2U).
Un nombre écrit avec un l à la fin symbolise un long. Exemple : 2l (ou 2L).
Un nombre écrit avec ul à la fin symbolise un ulong. Exemple : 2ul (ou 2UL).
Un nombre écrit avec une partie décimale, ou avec un d à la fin symbolise un double. Exemple : 2.0 ou 2d (ou 2D).
Un nombre écrit avec un f à la fin symbolise un float. Exemple : 2.0f ou 2f (ou 2F).
Un nombre écrit avec un m à la fin symbolise un decimal. Exemple : 2.0m ou 2m (ou 2M).

D'autre part, les guillemets (") servent à encadrer une chaîne de caractères et les apostrophes (') servent à encadrer un caractère.
Exemple : "Bonjour tout le monde" et 'B'.


Les variables Utiliser des variables

Utiliser des variables

Qu'est-ce que c'est ? Type valeur ou référence ?

Déclaration

Pour utiliser une variable, il faut d'abord la déclarer (la créer) : on réserve une partie de la mémoire pour cette variable. On spécifie ce qu'elle représentera (un entier, un caractère, une image, ...) en indiquant son type.
La syntaxe est : type nom;

Valeur par défaut

Une fois que cette ligne est exécutée, de la place est allouée en mémoire pour stocker une valeur.

Par exemple, le code suivant causera une erreur :

static void Main(string[] args)
{
	int myInt;
	Console.WriteLine(myInt);
}

Le compilateur vous dit "Use of unassigned local variable 'myInt'". En fait, le compilateur crie parce qu'il ne sait pas ce que doit valoir notre entier. Il faut donc que vous lui donniez une valeur, par exemple :

static void Main(string[] args)
{
	int myInt = 255;
	Console.WriteLine(myInt);
}

Comme je vous le disais, vous n'êtes pas toujours contraints à spécifier de valeur : dans certains cas, c'est la valeur par défaut qui est automatiquement utilisée. Nous étudierons cela dans le chapitre sur les classes, mais ça ne fait pas de mal d'apprendre dès maintenant les valeurs par défaut :

Attribuer une valeur

Voici comment créer un entier nommé myInteger et ayant la valeur 3 :

int myInteger;
myInteger = 3;

Pour raccourcir, écrivez : int myInteger = 3;.
Cela revient exactement au même : on rassemble la déclaration et l'initialisation. Vous conviendrez que c'est mieux car c'est plus concis.

Voici comment créer une chaîne de caractères valant "Hello world!" et un caractère ("char" signifie "caractère" en anglais) valant 'a' :

string myString = "Hello world!";
char myChar = 'a';

Qu'est-ce que c'est ? Type valeur ou référence ?

Type valeur ou référence ?

Utiliser des variables Les énumérations

Il existe deux sortes de types : les types valeur (notamment les structures) et les types référence (notamment les classes).

Type valeur

Une variable de type valeurcontient directement l'information. Cela signifie que lorsque cette variable est modifiée, la valeur qu'elle contenait est aussi modifiée.

Cela ne m'a pas paru indispensable de vous apprendre à créer des structures, par contre il faut que vous connaissiez des structures élémentaires. Les types numériques de base sont des structures, donc se comportent comme des types valeur. Le type string est un peu spécial : c'est une classe, mais elle se comporte comme un type valeur.

Type référence

Une variable de type référencecontient l'adresse de l'emplacement mémoire où se trouve l'information. Du coup, une variable qui a la même référence qu'une seconde variable peut être automatiquement changée quand la seconde variable est modifiée.

Nous verrons tout cela bien plus en détail avec les classes. Je voulais juste introduire cette notion car c'est quelque chose de fondamental et je pense que plus c'est pris tôt, plus vous aurez le temps d'y revenir petit à petit pour bien cerner le concept.


Utiliser des variables Les énumérations

Les énumérations

Type valeur ou référence ? Les opérateurs

Une énumération est une liste de valeurs qui a un type unique. Ce type est le nom de l'énumération.

Un exemple vaut toujours plus qu'un long discours donc voyez plutôt comment déclarer une énumération désignant les différents temps (dans l'ordre : inconnu, ensoleillé, nuageux, pluvieux) :

enum Weather
{
	Unknown,
	Sunny,
	Cloudy,
	Rainy
}

Chaque valeur doit être suivie d'une virgule, sauf la dernière pour laquelle ce n'est pas obligé (mais vous pouvez quand même le faire ; par contre, c'est moche :p ).
Vous pouvez aussi mettre un point-virgule après l'accolade fermante, mais ce n'est pas nécessaire pour la compilation.

Une fois que cela est fait, vous pouvez déclarer une variable de type Weather comme suit :

Weather todaysWeather = Weather.Sunny;

La variable todaysWeather est ici initialisée à la valeur Sunny.

Si vous ne faites que déclarer la variable, sans l'initialiser, elle prendra automatiquement la première valeur.
Ainsi, Weather todaysWeather; correspond ici à Weather todaysWeather = Weather.Unknown;.

Ce n'est pas dit que vous ayez toujours besoin des énumérations, mais sachez qu'elles sont très utiles quand même. Vous verrez des exemples dans la 2e partie.

namespace ConsoleApplication1
{
	class Program
	{
		enum Weather
		{
			Unknown,
			Sunny,
			Cloudy,
			Rainy
		}
		
		static void Main(string[] args)
		{
			Weather todaysWeather = Weather.Sunny;
		}
	}
}

Ça y est, vous savez ce que sont les variables. Retenez bien qu'il faut d'abord les déclarer, puis les initialiser, pour les utiliser.


Type valeur ou référence ? Les opérateurs

Les opérateurs

Les énumérations Les opérateurs mathématiques

Dans la quasi-totalité des cas, les opérateurs servent à effectuer des opérations avec des variables qui sont de même type.

Par exemple, comment voulez-vous comparer une image et un nombre ? Ce n'est pas faisable, nous sommes d'accord. ^^

Les opérateurs mathématiques

Les opérateurs Les opérateurs logiques

Ils s'utilisent sur des variables qui représentent en général des nombres.

Le +

Il sert à additionner deux nombres.

int myInt = 1 + 1;

Après exécution, myInt vaut 2.

Il peut aussi servir pour des chaînes de caractères :

string s = "deux " + "mots";

Après exécution, s vaut "deux mots".

Le -

Il sert à effectuer une soustraction.

int myInt = 3 - 4;

Après exécution, myInt vaut -1.

Le *

Il sert à effectuer une multiplication.

int myInt = 2 * 3;

Après exécution, myInt vaut 6.

Le /

Il sert à effectuer une division.

int myInt = 5 / 2;

Ainsi, après exécution, myInt vaut 2.

double d = 5.0 / 2;

Après exécution, d vaut 2.5. Le fait d'avoir mis un point (qui correspond en français à une virgule) indique que l'on ne veut pas traiter avec des entiers. 2 est bien un entier mais est, ici, automatiquement converti pour que l'opération fonctionne.

Le %

Il permet d'obtenir le reste d'une division euclidienne : si a = b*q + r, avec 0 <= r < q, alors a % b vaut r.

int myInt = 21 % 10;

Après exécution, myInt vaut 1 car 21 = 2*10 + 1.

int i = 5;
int j = i + 7;

Après exécution, i vaut 5 et j vaut 5 + 7 = 12.

Simplifications d'écriture

Il est possible (et préférable) d'écrire :

Pour simplifier au maximum votre code, plutôt que d'écrirei = i + 1; ou i += 1;écrivezi++;

Priorités des opérateurs

+ et - ont une priorité rigoureusement identique et sont évalués dans l'ordre d'apparition (il en va de même pour * et /, qui en plus sont prioritaires devant + et -). Dans le doute, mettez des parenthèses pour être sûrs des priorités. Pensez aussi à ceux qui vous reliront peut-être : codez de façon claire.


Les opérateurs Les opérateurs logiques

Les opérateurs logiques

Les opérateurs mathématiques Autres opérateurs

En électronique, ils permettent de comparer deux bits ensemble. Un bit vaut soit 0 (pas de tension) soit 1 (présence de tension).

En C#, ils permettent de comparer deux booléens. Le terme "booléen" vient de George Boole, mathématicien anglais à qui on doit l'algèbre de Boole : théories mathématiques sur les comparaisons d'assertions (une assertion est une proposition pouvant être soit vraie, soit fausse).

En termes binaires:
0 correspond à false (faux)
1 correspond à true (vrai)

Les opérateurs sont représentés par des symboles (ici entre parenthèses). Dans le code vous devez impérativement utiliser les symboles et non pas le nom des opérateurs.

NOT (!)

Opérande

Résultat

false

true

true

false

Le résultat est l'inverse de l'opérande.
Ce qui donne en notation binaire :
!0 = 1
!1 = 0

AND (&&)

Opérandes

Résultat

false

false

false

false

true

false

true

false

false

true

true

true

Les deux opérandes doivent valoir true pour que le résultat soit true.
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

D'un point de vue électronique, on peut écrire "+" à la place de "OR" et "." (point de la multiplication) à la place de "AND". Vous pouvez regarder des articles sur l'algèbre de Boole pour apprendre à maîtriser les opérateurs logiques.

OR (||)

Opérandes

Résultat

false

false

false

false

true

true

true

false

true

true

true

true

Un au moins des deux opérandes doit valoir true pour que le résultat soit true.
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

XOR (^)

Opérandes

Résultat

false

false

false

false

true

true

true

false

true

true

true

false

C'est un ou exclusif : pareil que pour OR mais si les deux sont true, le résultat sera false.
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0


Les opérateurs mathématiques Autres opérateurs

Autres opérateurs

Les opérateurs logiques Les conditions et les boucles conditionnelles

L'opérateur d'affectation

Le =

La syntaxe est : variableÀModifier = nouvelleValeurDeLaVariable;
Par exemple, si myInt est un entier, l'instruction myInt = 2; remplace la valeur existante de myInt par 2.

Lignes de code

Résultat

int myInt;

Un espace mémoire est alloué pour stocker la valeur de myInt. À cet instant myInt n'a pas de valeur.

(lignes intermédiaires)

myInt = 2;

La valeur de myInt est modifiée. myInt vaut à présent 2.

Pour faire une assignation (=affectation), on peut aussi utiliser une autre variable ; la syntaxe est alors : variableÀModifier = autreVariable;

Que se passe-t-il à l'exécution ?

En fait cela dépend. Si les variables sont de type valeur, on récupère la valeur de autreVariable et on l'écrit en mémoire là où est stockée variableÀModifier.
Si autreVariable ne contient pas une valeur mais une référence, alors les deux variables variableÀModifier et autreVariable vont référencer le même objet.
Après l'affectation variableÀModifier = autreVariable;, si l'une des deux variables est modifiée, l'autre le sera donc aussi.

Les opérateurs de comparaison

Opérateur ==

Il retourne true si les deux opérandes sont égaux et false si ce n'est pas le cas.

bool numbersAreEqual = (2 == 5);

2 n'est pas égal à 5 donc après exécution numbersAreEqual vaut false.

Opérateur !=

Il retourne true si les deux opérandes sont différents et false si ce n'est pas le cas.

bool numbersAreDifferent = (2 != 5);

2 n'est pas égal à 5 donc après exécution numbersAreDifferent vaut true.

Opérateurs d'inégalités

Vous les connaissez, ce sont des opérateurs mathématiques simples :

L'opérateur ternaire "?"

Il s'utilise de cette façon : a ? b : c;.
a doit être une expression conditionnelle (qui retourne true ou false). À l'exécution, cette expression est évaluée.
Si elle vaut true, l'expression b sera exécutée et son résultat sera retourné ; l'expression c sera ignorée.
Si elle vaut false, l'expression c sera exécutée et son résultat sera retourné ; l'expression b sera ignorée.

Voici un exemple :

string myString = (2 == 5) ? "les nombres sont égaux" : "les nombres ne sont pas égaux";

Après exécution, myString vaut "les nombres ne sont pas égaux" car (2 == 5) a retourné false.

Mémorisez bien tous ces hiéroglyphes car vous en aurez tout le temps besoin !


Les opérateurs logiques Les conditions et les boucles conditionnelles

Les conditions et les boucles conditionnelles

Autres opérateurs if et sa famille

Dans un programme, tout le code ne s'exécute pas forcément. Vous pouvez faire s'exécuter certaines parties du code si des conditions sont remplies.

La route me semble être une bonne analogie. Il n'y a pas qu'un seul chemin à emprunter pour se rendre à un endroit. Suivant le lieu où vous désirez vous rendre, vous n'allez pas faire la même chose. Eh bien ici c'est pareil ! ;)

if et sa famille

Les conditions et les boucles conditionnelles switch : un if spécial

if

"if" est un mot anglais qui signifie "si". C'est un mot-clef du langage C# qui permet de n'effectuer du code que dans certaines conditions.
La syntaxe est la suivante :

if (condition)
{
	// Code à exécuter si la condition est satisfaite.
}

condition est une expression qui retourne un booléen : soit true, soit false :

Commenter judicieusement son code est un travail subtil. Il faut savoir expliquer ce qui est susceptible de ne pas être compris par un relecteur, tout en ne tombant pas dans l'excès (c'est-à-dire ne pas commenter chaque ligne, ou du code basique).

else if

"else if" veut dire "sinon, si". Placé après un if, il permet d'exécuter du code qui ne remplit pas la première condition, mais la deuxième.
La syntaxe est la suivante :

if (condition1)
{
	// Code à exécuter si la condition est satisfaite.
}
else if (condition2)
{
	// Code à exécuter si la première condition n'est pas satisfaite mais que la deuxième l'est.
}

Vous pouvez mettre autant de else if que vous voulez.

else

"else" signifie "sinon". Placé après un if simple, ou un if et un (ou plusieurs) else if, il permet d'exécuter le code qui ne satisfait aucune condition précédente.
La syntaxe est la suivante :

if (condition)
{
	// Code à exécuter si la condition est satisfaite.
}
else
{
	// Code à exécuter si la première condition n'est pas satisfaite.
}

Les trois expressions peuvent se combiner. Voici un exemple où age est un entier représentant l'âge d'un client à un restaurant :

if (age < 4)
{
	// gratuit
	// 0 < age < 4
}
else if (age < 12)
{
	// Tarif enfant
	// 4 <= age < 12
	// en effet, age n'est pas strictement inférieur à 4, et est strictement inférieur à 12.
}
else if (age < 18)
{
	// Tarif ado
	// 12 <= age < 18
}
else
{
	// Tarif adulte
	// 18 <= age
}

// suite du code

Les conditions et les boucles conditionnelles switch : un if spécial

switch : un if spécial

if et sa famille while

Il permet de remplacer if et sa famille pour étudier l'égalité de plusieurs termes.

Voici un exemple avec iflevel est un entier :

if (level == 1)
{
	// niveau 1
}
else if (level == 2)
{
	// niveau 2
}
else
{
	// autre niveau
}

Voici ce que ça donne avec switch :

switch (level)
{
    case 1:
    {
        // niveau 1
    }
    break;

    case 2:
    {
        // niveau 2
    }
    break;

    default:
    {
        // autre niveau
    }
    break;
}

Là-aussi, vous pouvez mettre autant de case que vous voulez.


if et sa famille while

while

switch : un if spécial for

Les boucles conditionnelles permettent de répéter des actions tant que certaines conditions sont satisfaites. Dès lors que ces dernières ne le sont plus, le programme "sort de la boucle" et exécute le code qui suit.

while

"while" veut dire "tant que".
La syntaxe est :

while (condition)
{
	// Code qui s'exécute tant que la condition est satisfaite.
}
while (condition)
{
    if (needToExit)
    {
        break;
    }

    // Code à exécuter en boucle
}

Une fois que break est exécuté, plus aucune ligne de code à l'intérieur des accolades de la boucle n'est exécutée. Ainsi la ligne qui s'exécute après le break est celle située juste après l'accolade fermante.

Puisqu'un exemple permet toujours de mieux comprendre, je vous propose un bout de code pour faire un compte à rebours qui débute à 100 :

int i = 100;
while (i > 0)
{
	// On décrémente i
	i--;
}

do ... while

L'équivalent français serait "fait ... tant que". Cela permet d'exécuter les instructions une première fois obligatoirement, et de répéter l'exécution si besoin est.
La syntaxe est :

do
{
	// Code qui s'exécute une 1ère fois obligatoirement et ensuite en boucle tant que la condition est satisfaite.
}
while (condition);

switch : un if spécial for

for

while Les méthodes

C'est un while compact.

Regardez ce que devient notre compte à rebours avec for :

int i;
for (i = 100; i > 0; i--)
{
}

On peut tout aussi bien écrire :

for (int i = 100; i > 0; i--)
{
}

continue

À la différence de break;, continue; ne s'utilise pas pour terminer la boucle, mais pour passer directement à l'élément suivant. Voici sans plus attendre un exemple, qui vous apportera plus qu'un long discours :

for (int i = 1; i <= 10; i++)
{
	if (i < 9)
	{
		continue;
	}
	// La ligne suivante n'est pas exécutée si i est strictement inférieur à 9.
	Console.WriteLine(i);
}

Vous verrez donc :

9
10

Notez bien que la boucle ne s'arrête pas, seules les instructions relatives à l'élément en cours ne sont plus exécutées.

Les tests de conditions sont vraiment très courants ils sont présents même dans les morceaux de code les plus simples. Ce chapitre est donc un incontournable de plus !


while Les méthodes

Les méthodes

for Créer une méthode

Les méthodes (aussi appelées "fonctions" dans de nombreux langages) sont là pour nous simplifier la vie : plutôt que de mettre tout le code à la suite, on structure notre programme en créant des méthodes.

Une méthode est une suite d'instructions regroupées sous un nom ; elle prend en entrée des paramètres et retourne un résultat. Notez qu'une méthode peut ne prendre aucun paramètre en entrée ou ne retourner aucun résultat.

Les méthodes ont de nombreux avantages. Le premier est sans doute de ne pas avoir à réécrire beaucoup de lignes de code à chaque fois qu'on veut faire une certaine opération. Cela permet aussi de rajouter un aspect dynamique au code : suivant les paramètres passés, le résultat retourné n'est pas le même.

Créer une méthode

Les méthodes Appeler une méthode

Voici la syntaxe pour une méthode prenant deux paramètres en entrée et ne retournant rien :

void nomDeLaMéthode(typeDu1erParamètre nomDu1erParamètre, typeDu2eParamètre nomDu2eParamètre)
{
    // Code à exécuter quand la méthode est appelée.
}

Voici la syntaxe pour une méthode ne prenant aucun paramètre en entrée et retournant xxx (xxx étant une variable) :

typeDuRésultat nomDeLaMéthode()
{
    // Code à exécuter quand la méthode est appelée.
    return xxx;
}
void

veut dire "vide", "dépourvu de", ... Quand une méthode ne retourne rien, le type de retour est donc void.

Besoin d'exemples ?

Pas de soucis ! Créons une méthode qui multiplie un nombre x par un nombre y (nous allons travailler avec des entiers). Voici comment faire :

int Multiply(int x, int y)
{
    return x*y;
}

Nous allons utiliser une méthode déjà créée qui va afficher du texte dans la console, c'est WriteLine. Cette méthode se situe dans la classe Console, donc pour y accéder il faut écrire Console.WriteLine. Vous aurez plus d'explications quand nous verrons les classes et vous pourrez ainsi mieux comprendre. ;) Pour l'instant, regardez comment ça fonctionne dans les exemples suivants.

Voici une autre méthode, qui affiche un Hello World! dans le cadre "Output" de Visual Studio si vous avez un programme avec des fenêtres (par exemple des WinForms), et dans la console dans le cas contraire.

Rappel : la console, plus connue sous le nom d'éditeur de commandes, est la fenêtre où du code est écrit en général en vert sur fond noir.

void SayHello()
{
    Console.WriteLine("Hello world!");
}

Vous noterez la magnifique transition vers l'appel d'une méthode. Eh oui, Console.WriteLine("Hello world!"); est bien un appel une méthode !


Les méthodes Appeler une méthode

Appeler une méthode

Créer une méthode La surcharge

Console.WriteLine("Hello world!"); est un appel à la la méthode WriteLine, à laquelle nous passons en paramètre une chaîne de caractères qui vaut "Hello world!".

L'appel se fait comme ceci : nomDeLaMéthode(paramètresSéparésParUneVirgule);.

Pourquoi c'est Console.WriteLine au lieu de simplement WriteLine ?

Console est une classe dans laquelle est définie la méthode WriteLine. Ne nous attardons pas sur ce point qui fera l'objet d'explications détaillées dans la suite de cette 1ère partie théorique.

On pourra donc écrire (si l'on a créé la méthode Multiply) :

int number1 = 3;
int number2 = 2;
int number3 = Multiply(number1, number2);
Console.WriteLine(number3);

Que se passe-t-il à la ligne no3 ?

number1 et number2 sont évalués : on va récupérer en mémoire leur valeur, que l'on passe à la méthode. La méthode fait le calcul et renvoie le résultat (ici 6). L'expression Multiply(number1, number2) est donc évaluée comme un entier valant 6. Ensuite on fixe la valeur de number3 à 6.

Pour simplifier, écrivez :

Console.WriteLine(Multiply(3, 2));

Comme bien souvent, le code se lit de droite à gauche.
On appelle la méthode Multiply à laquelle on passe les nombres 3 et 2, compris par défaut comme des entiers. Multiply passe son résultat en tant que paramètre à WriteLine qui affichera donc 3*2, soit 6.

Passage par valeur

Par défaut, les paramètres des méthodes sont toujours passés par valeur. Cela signifie qu'une copie de la valeur de chaque paramètre est faite au moment de l'appel et ce sont ces copies qui sont utilisées dans le corps de la méthode : les variables transmises à la méthode ne peuvent donc pas être modifiées par celle-ci. Si vous modifiez ces valeurs dans le corps de la méthode, les modifications seront perdues à la fin de son appel.

En réalité ce comportement n'a rien de sorcier : c'est exactement ce qui se passe lorsque vous initialisez une variable à partir d'une autre variable. La valeur est copiée au moment de l'initialisation, puis les deux variables vivent leur vie indépendamment. Voici un exemple rapide pour le prouver :

int myInt = 100;
// On copie la valeur, donc myOtherInt vaut 100.
int myOtherInt = myInt;
Console.WriteLine("myInt = " + myInt);
Console.WriteLine("myOtherInt = " + myOtherInt);

// On modifie myOtherInt.
myOtherInt = 5;
Console.WriteLine("myInt = " + myInt);
Console.WriteLine("myOtherInt = " + myOtherInt);

Après exécution, vous aurez :

myInt = 100
myOtherInt = 100
myInt = 100
myOtherInt = 5

Pas de surprise donc. ;)

Introduisons maintenant un petit peu de vocabulaire pour aider à comprendre la suite :

Les paramètres formels doivent avoir le même type que les paramètres effectifs (ou un type compatible), mais pas nécessairement le même nom.
Voyons ce qui se passe quand on modifie la valeur d'un paramètre formel. Voici une méthode ChangeInt qui prend un entier en paramètre, le modifie, et affiche son contenu :

void ChangeInt(int myParameter)
{
	Console.WriteLine("Le paramètre formel myParameter vaut : " + myParameter);
	myParameter = 5;
	Console.WriteLine("Le paramètre formel myParameter vaut : " + myParameter);
}

Voici comment vous pouvez l'utiliser :

int myInt = 100;
Console.WriteLine("Le paramètre effectif myInt vaut : " + myInt);
ChangeInt(myInt);
Console.WriteLine("Le paramètre effectif myInt vaut : " + myInt);

Après exécution, vous aurez :

Le paramètre effectif myInt vaut : 100
Le paramètre formel myParameter vaut : 100
Le paramètre formel myParameter vaut : 5
Le paramètre effectif myInt vaut : 100
myInt

n'a pas été changé car on n'a fait que modifier myParameter, qui ne contenait qu'une copie de sa valeur. Le comportement observé est le même que dans l'exemple donné précédemment.

Il peut cependant être utile de modifier un paramètre effectif depuis une méthode, c'est-à-dire de répercuter les changements sur le paramètre qui a été transmis. Pour cela, on utilise le passage par référence.

Passage par référence

Lorsqu'un paramètre est passé par référence, il est lui-même utilisé dans le corps de la méthode. Les paramètres effectif et formel ne font plus qu'un. Il est donc possible de les modifier dans le corps de la méthode et les changements seront maintenus après l'appel.

Pour forcer le passage par référence, utilisez le mot-clef ref.

ref

Reprenons l'exemple en modifiant la méthode ChangeInt :

// Notez l'ajout de ref.
void ChangeInt(ref int myParameter)
{
	Console.WriteLine("Le paramètre formel myParameter vaut : " + myParameter);
	myParameter = 5;
	Console.WriteLine("Le paramètre formel myParameter vaut : " + myParameter);
}

Voici comment vous pouvez l'utiliser :

int myInt = 100;
Console.WriteLine("Le paramètre effectif myInt vaut : " + myInt);
// On utilise ref aussi pour l'appel.
ChangeInt(ref myInt);
Console.WriteLine("Le paramètre effectif myInt vaut : " + myInt);

Après exécution, vous aurez :

Le paramètre effectif myInt vaut : 100
Le paramètre formel myParameter vaut : 100
Le paramètre formel myParameter vaut : 5
Le paramètre effectif myInt vaut : 5

L'utilisation de ref impose que le paramètre effectif soit une variable correctement initialisée. Vous ne pourrez donc pas compiler les exemples de code suivant :

int myInt = 5;
// Erreur du compilateur : "Argument 1 must be passed with the 'ref' keyword".
ChangeInt(myInt);
// On ne fait que déclarer l'entier, sans l'initialiser.
int myInt;
// Erreur du compilateur : "Use of unassigned local variable 'myInt'".
ChangeInt(ref myInt);
// On essaie de transmettre une valeur directement (ici la valeur 5).
// Erreur du compilateur : "A ref or out argument must be an assignable variable".
ChangeInt(ref 5);

Il peut arriver qu'un paramètre serve uniquement à récupérer une valeur initialisée par la méthode. C'est notamment utile lorsqu'une méthode doit renvoyer plusieurs valeurs. Pour ces cas-là, on utilise le mot-clef out.

out

L'utilisation de out impose à la méthode d'assigner une valeur au paramètre avant de se terminer. Et contrairement à ref, il n'est pas obligatoire d'assigner une valeur au paramètre avant de le transmettre à la méthode : la valeur transmise ne sera de toute façon pas utilisable dans la méthode.

Reprenons le même exemple, en utilisant out cette fois :

// Notez l'utilisation de out.
void ChangeInt(out int myParameter)
{
	// On ne peut pas afficher la valeur de myParameter avant la prochaine ligne de code,
	// car il serait considéré comme non assigné. On réalise alors l'assignation.
	myParameter = 5;
	Console.WriteLine("Le paramètre formel myParameter vaut : " + myParameter);
}

Voici comment vous pouvez l'utiliser :

// On ne fait que déclarer l'entier, sans l'initialiser.
int myInt;
// On utilise out aussi pour l'appel.
ChangeInt(out myInt);
Console.WriteLine("Le paramètre effectif myInt vaut : " + myInt);

Après exécution, vous aurez :

Le paramètre formel myParameter vaut : 5
Le paramètre effectif myInt vaut : 5

En pratique, out est utilisé beaucoup plus souvent que ref, car il arrive fréquemment qu'une méthode doive retourner plus d'une valeur. Par exemple, la méthode int.TryParse sert à convertir une chaîne de caractères en entier. Elle renvoie deux valeurs : un entier (le résultat de la conversion), et un booléen (qui indique si la conversion a réussi ou non). Elle s'utilise comme ceci :

int myInt;
bool success = int.TryParse("1234", out myInt);
if(success)
{
	Console.WriteLine("La conversion a réussi, le double du nombre est : " + myInt * 2 + ".");
}
else
{
	Console.WriteLine("La conversion a échoué.");
}

Créer une méthode La surcharge

La surcharge

Appeler une méthode Les classes (1/2)

La surcharge permet de créer des méthodes qui font globalement la même chose en leur donnant le même nom.

Elles doivent cependant avoir des signatures différentes. La signature d'une méthode permet de l'identifier ; elle dépend de deux choses : son nom et ses arguments (mais pas son type de retour !). Pour la surcharge, les méthodes doivent donc être différentiables par au moins une des propositions suivantes :

Dans l'exemple suivant, on crée deux méthodes SayHello dont l'une, une fois appelée, affichera juste "Hello!" (on ne lui passe aucun paramètre), et l'autre affichera par exemple "Hello John!" si on lui passe en paramètre la chaîne de caractères "John" :

void SayHello()
{
	Console.WriteLine("Hello!");
}

void SayHello(string name)
{
	Console.WriteLine("Hello " + name + "!");
}

Je sais que je n'arrête pas de vous le dire, mais là encore, c'est une notion fondamentale ! Elle existe d'ailleurs dans de nombreux langages (dans lesquels on utilise plus le mot "fonction" que "méthode").

Vous verrez en codant que les méthodes se révèlent très utiles. ;)


Appeler une méthode Les classes (1/2)

Les classes (1/2)

La surcharge Créer une classe

Vous en entendez parler depuis longtemps et à chaque fois je vous dit que ça viendra plus tard.
J'ai une bonne nouvelle pour vous : nous allons dès à présent attaquer ce point incontournable du C#.

:pirate: Let's go ! (Allons-y :-° )

Créer une classe

Les classes (1/2) Créer un objet

Présentation

Une classe est tout simplement un moule pour faire des objets.

Un objet est composé de membres ; parmi ces membres, on dispose de champs (les variables qui lui sont caractéristiques), de méthodes, de propriétés, ainsi que d'autres éléments que nous verrons plus tard. On a tendance à croire que "champ" et "membre" désignent la même chose alors que c'est faux : il faut bien voir qu'il existe plusieurs sortes de membres, dont les champs.

C'est dans la classe que sont définis les membres (dont les champs et les méthodes). Tout objet créé à partir d'une classe possède les membres que propose cette classe, vous comprenez donc pourquoi je parle de "moule à objet".

Une classe simple se présente sous cette forme :

class nomDeLaClasse
{
    // Déclaration des champs

    // Déclaration des méthodes
}

Les champs sont de simples variables, vous savez donc les déclarer. ;)

Le constructeur

C'est le nom que l'on donne à une méthode spéciale dans une classe. Le constructeur (c'est aussi un membre) d'une classe est appelé à chaque fois que vous voulez créer un objet à partir de cette classe. Vous pouvez donc écrire du code dans cette méthode et il sera exécuté à chaque création d'un nouvel objet.

Pour filer la métaphore du moule, les objets seraient les gâteaux que l'on peut faire avec et le constructeur serait en quelque sort notre cuisinier.

Lorsqu'il est appelé, le constructeur réserve un emplacement mémoire pour votre objet et si vous n'avez pas initialisé ses champs, il les initialise automatiquement à leur valeur par défaut.

Sachez aussi que vous n'êtes pas obligés d'écrire vous-mêmes le code du constructeur ; dans ce cas, un constructeur "par défaut" est utilisé. Si vous faites ainsi, lorsque l'objet est créé, tous ses champs qui ne sont pas déjà initialisés dans le code de la classe sont initialisés à leur valeur par défaut.

L'intérêt du constructeur est d'offrir au développeur la possibilité de personnaliser ce qui doit se passer au moment de la création d'un objet. Il rajoute en outre un aspect dynamique au code : vous pouvez affecter vos champs à l'aide de variables passées en paramètres.

Le destructeur

Le destructeur (c'est aussi un membre) est une méthode appelée lors de la destruction d'un objet. Son nom est celui de la classe, précédé d'un tilde '~'.
Là-aussi, rien ne vous oblige à mettre un destructeur. C'est seulement si vous voulez faire quelque chose de particulier à sa destruction. Cela peut notamment servir à libérer de la mémoire et à bien gérer les ressources ; c'est bien trop compliqué pour l'instant alors nous en parlerons en temps et en heure.

Exemple

Nous allons étudier une classe Person (qui représente une personne, vous l'aurez deviné :-° ). Dans cet exemple, vous ne comprendrez pas le code à la première lecture ; pas d'affolement, les explications arrivent juste après. ;)

public class Person
{
	private string m_name;
	public string Name
	{
		get { return m_name; }
		set { m_name = value; }
	}

	private ushort m_age;
	public ushort Age
	{
		get { return m_age; }
		set { m_age = value; }
	}

	public Person()
	{
		Console.WriteLine("Nouvelle personne créée.");
	}

	public Person(string name, ushort age)
	{
		this.m_age = age;
		this.m_name = name;
		Console.WriteLine("Nouvelle personne créée. Cette personne s'appelle " + name + " et a " + age + " an(s).");
	}

	~Person()
	{
		Console.WriteLine("Objet détruit.");
	}

	public void SayHi()
	{
		Console.WriteLine("Bonjour ! Je m'appelle " + this.m_name + " et j'ai " + this.m_age + " ans.");
	}
}

Je me dois de vous expliquer quelques nouveautés.

Les modificateursprivate et public se mettent devant un type (par exemple : une classe) ou un membre (par exemple : un champ ou une méthode).
private restreint l'accès de ce qui suit à l'usage exclusif dans le bloc où il a été déclaré.
public autorise quant à lui l'accès de ce qui suit depuis l'extérieur.
Le constructeur doit impérativement être précédé de public si vous voulez pouvoir l'appeler et créer un objet.
Par défaut, champs et méthodes utilisent le modificateur private, mais pour bien voir ce que l'on fait, il est préférable de toujours préciser. Nous verrons plus tard qu'il existe d'autres possibilités que public pour les classes elles-mêmes, mais ne nous y attardons pas pour l'instant.

Vous pouvez cependant accéder publiquement à des champs privés, en ayant recours à des propriétés, comme dans cette classe avec la propriété Age :

public ushort Age
{
	get { return m_age; }
	set { m_age = value; }
}

À l'intérieur du bloc get, vous définissez comment se fait l'accès en lecture. Dans ce cas, si l'on veut récupérer la valeur de l'âge, on pourra écrire ushort userAge = toto.Age; (toto étant un objet de type Person).

À l'intérieur du bloc set, vous définissez comment se fait l'accès en écriture. Dans ce cas, si l'on veut changer la valeur de l'âge, on pourra écrire toto.Age = 10; (10 étant ici implicitement converti en ushort).

Que vient faire value dans tout ça ?

La variable value représente la valeur que l'on veut donner à m_age. Si l'on écrit toto.Age = 10;, value est un ushort qui vaut 10.

Les propriétés ont un statut particulier ; en fait ce ne sont pas des variables mais des moyens d'accéder à des variables. D'ailleurs, get et set sont ce qu'on appelle des accesseurs. Utiliser une propriété en définissant ses accesseurs revient exactement à créer une méthode d'accès en lecture (que l'on peut ici nommer GetAge) et une méthode d'accès en écriture (que l'on peut ici nommer SetAge) :

public ushort GetAge()
{
	return m_age;
}

public void SetAge(ushort value)
{
	m_age = value;
}

J'ai sciemment employé le nom "value" pour que vous voyiez comment la variable value est traitée dans le code d'une propriété. Autant j'aurais pu nommer ce paramètre différemment, autant dans un bloc set on est obligé d'utiliser le nom "value" (si on n'avait pas de nom standardisé, le compilateur ne pourrais pas s'en sortir !).

Revenons à l'exemple de classe que je vous ai fourni. J'ai créé deux méthodes portant le même nom Person. Ce n'est pas une erreur, en fait j'ai surchargé le constructeur.

this est un mot-clef du langage C# qui désigne l'objet lui-même ("this" veut dire "ceci" en anglais). L'écriture this.m_age permet d'accéder au champ m_age de l'objet désigné par this.
this.m_age = age; aura pour effet d'initialiser l'âge de ma nouvelle personne avec l'entier que je passe comme paramètre au constructeur.


Les classes (1/2) Créer un objet

Créer un objet

Créer une classe Utiliser un objet

Rappelez-vous, en introduisant les variables nous avons évoqué l'existence de variables de type valeur et de variables de type référence. Jusqu'à présent, nous n'avions rencontré que des types valeurs (int, string, etc.). Comme les classes sont des types références, il est temps de s'y intéresser plus en détail.

Qu'est-ce qu'une variable de type référence ?

Une variable de type référence (ou tout simplement, une référence) est une variable dont la valeur est l'adresse d'un emplacement mémoire.
Cet emplacement contient des informations utiles à notre programme, comme par exemple une instance de classe. Les habitués du C/C++ retrouveront beaucoup de similitudes avec le concept de pointeur. ^^

En quoi cela va-t-il nous servir ?

Un objet est toujours issu d'une classe. On dit que c'est une instance de cette classe. La particularité des instances de classe est qu'elles se baladent toujours quelque part dans la mémoire, plus librement que les autres variables. Nous avons donc besoin d'une référence pour savoir où elles se trouvent et ainsi pouvoir les manipuler. Contrairement aux variables de type valeur qui contiennent une valeur que l'on manipule directement, les références ne font que désigner une instance qui, elle, peut contenir une ou plusieurs valeurs. L'accès à ces valeurs se fait donc indirectement.

Pas de panique, c'est plus simple que ça en a l'air :) Voyons ce que ça donne en pratique avec la classe Person que nous avons définie plus haut.

Pour manipuler une instance de cette classe, je vais devoir faire deux choses :

  1. déclarer une référence qui servira à désigner mon instance ;

  2. créer mon instance, c'est-à-dire instancier ma classe.

1. Déclarer une référence

Une référence est avant tout une variable, et se déclare comme toutes les variables. Son type est le nom de la classe que l'on compte instancier :

Person toto;

On vient de déclarer une référence, appelée toto, qui est prête à désigner un objet de type Person. :)

Quelle est la valeur de cette référence?

Nous n'avons fait que déclarer une référence sans préciser de valeur ; elle a dans ce cas été initialisée à sa valeur par défaut, qui est null pour les références. Ce code est donc équivalent à :

Person toto = null;

Lorsqu'une référence vaut null, cela signifie qu'elle ne désigne aucune instance. Elle est donc inutilisable. Si vous tentez de vous en servir, vous obtiendrez une erreur à la compilation :

Person toto;
// Erreur à la compilation: "Use of unassigned local variable 'toto'".
toto.SayHi();

Le compilateur vous indique que vous essayez d'utiliser une variable qui n'a pas été assignée. Rappelez-vous, nous avons vu ça dans le chapitre "Les variables", paragraphe "Utiliser des variables".

En revanche, vous pouvez tout à fait déclarer un champ sans l'instancier : le constructeur de la classe se charge tout seul d'instancier à leur valeur par défaut tous les champs qui ne sont pas déjà instanciés. C'est pourquoi dans la classe Person ci-dessus, j'ai pu écrire private string m_name; sans écrire private string m_name = string.Empty; ou encore private string m_name = "";.

Petite piqure de rappel sur les valeurs par défaut :
La valeur par défaut de tout type numérique est 0, adapté au type (0.0 pour un float ou un double).
La valeur par défaut d'une chaîne de caractères (string) est null, et non pas la chaîne vide, qui est représentée par string.Empty ou encore "".
La valeur par défaut d'un caractère (char) est '\0'.
La valeur par défaut d'un objet de type référence est null.

Peut-on initialiser une référence avec une adresse mémoire explicite (comme en C/C++) ?

Non, de base c'est impossible en C# ; pour le faire, il faut utiliser le mot-clef unsafe, mais c'est une notion avancée et nous aurons peut-être l'occasion de la rencontrer plus tard. De base, il n'est d'ailleurs pas non plus possible de lire l'adresse contenue dans une référence.
Voyons alors comment assigner une valeur à notre référence.

2. Instancier une classe

Pour instancier la classe Person, et ainsi pouvoir initialiser notre référence avec une nouvelle instance, on utilise le mot-clef new :

// Déclaration de la référence.
Person toto;
// Instanciation de la classe.
toto = new Person();

Comme pour toute initialisation de variable, on peut fusionner ces deux lignes :

// Déclaration + instanciation
Person toto = new Person();

À ce stade nous avons créé une nouvelle instance de la classe Person, que nous pouvons manipuler grâce à la référence toto. :)

Lorsque l'opérateur new est utilisé, le constructeur de la classe est appelé. Nous avons vu qu'il pouvait y avoir plusieurs surcharges du constructeur dans une même classe, comme c'est le cas dans la classe Person. La version du constructeur appelée grâce à new est déterminée par les paramètres spécifiés entre les parenthèses.
Jusqu'à présent nous nous sommes contentés d'écrire new Person(), et nous avons ainsi utilisé implicitement le constructeur sans paramètre (on l'appelle constructeur par défaut).

La classe Person possède un autre constructeur qui nous permet de préciser le nom et l'âge de la personne. Profitons-en pour préciser que toto s'appelle Toto et a 10 ans, au moment de créer notre instance :

Person toto = new Person("Toto", 10);

Il était aussi possible de faire cela en plusieurs temps, comme ceci :

Person toto = new Person();
toto.Name = "Toto";
toto.Age = 10;

Si vous ne comprenez pas tout de suite la syntaxe des deux dernières lignes du bout de code précédent, c'est normal : nous n'avons pas encore vu comment utiliser des objets (ça ne saurait tarder).

Maintenant que nous savons créer des objets, voyons plus en détail comment nous en servir. ;)


Créer une classe Utiliser un objet

Utiliser un objet

Créer un objet Les classes (2/2)

Accéder aux membres d'un objet

Un fois l'objet créé, pour accéder à ses membres il suffit de faire suivre le nom de l'objet par un point.
toto.m_age n'est pas accessible car m_age est défini avec private. On peut en revanche accéder à la propriété publique toto.Age et à la méthode publique toto.SayHi.

Voyons ce que donne la méthode SayHi() de notre ami Toto :

Person toto = new Person("Toto", 10);
toto.SayHi();

Et nous voyons apparaître comme prévu :

Bonjour ! Je m'appelle Toto et j'ai 10 ans.

Les propriétés

Pourquoi utiliser des propriétés alors que l'on peut utiliser public à la place de private ?

Cela permet de rendre le code plus clair et d'éviter les erreurs. On laisse en général les champs en private pour être sûr qu'ils ne seront pas modifiés n'importe comment.
Ensuite, on peut néanmoins vouloir accéder à ces champs. Si tel est le cas, on utilise les propriétés pour contrôler l'accès aux champs. Dans certains langages, on parle d'accesseurs. En C#, les accesseurs sont get et set. Ils sont utilisés au sein d'une propriété.

Je reprends l'exemple ci-dessus :

private ushort m_age;
public ushort Age
{
	get { return m_age; }
	set { m_age = value; }
}

Comme je vous l'ai dit plus haut, get gère l'accès en lecture alors que set gère l'accès en écriture. Si vous ne voulez pas autoriser l'accès en écriture, il suffit de supprimer le set :

private ushort m_age;
public ushort Age
{
	get { return m_age; }
}

Le 1er morceau de code peut se simplifier en utilisant les accesseurs auto-implémentés :

public ushort Age { get; set; }

Dans ce cas, vous n'avez plus besoin de m_age. Le compilateur comprend que vous autorisez l'accès en lecture et en écriture.

// Erreur du compilateur :
// 'ConsoleApplication1.Person.Age.get' must declare a body because it is not marked abstract or extern.
// Automatically implemented properties must define both get and set accessors.
public ushort Age { get; }

Comment faire si je ne veux pas autoriser l'accès en écriture dans le code simplifié ?

Il suffit de rajouter private devant set pour indiquer que Age n'aura le droit d'être modifié qu'à l'intérieur de la classe :

public ushort Age { get; private set; }

Dans l'écriture simplifiée, il n'est plus question de m_age. Considérons le code suivant :

private ushort m_age;
public ushort Age { get; private set; }

Si je modifie Age, cela ne va pas affecter m_age ! En effet, dans l'écriture simplifiée on ne fait pas de lien entre m_age et Age : ils vivent leur vie chacun de leur côté.

La comparaison d'objets

Vous pouvez comparer des objets de diverses manières suivant ce que vous voulez vérifier.

Peut-on utiliser l'opérateur == ?

Oui et non ; en fait cela dépend de ce que vous voulez savoir. Considérons l'exemple suivant :

Person p1 = new Person("Toto", 10);
Person p2 = new Person("Toto", 10);
Person p3 = p1;

Dans cet exemple, p1 n'est pas égal à p2, mais p1 est égal à p3 :

Console.WriteLine(p1 == p2 ? "p1 est égal à p2." : "p1 n'est pas égal à p2.");
Console.WriteLine(p1 == p3 ? "p1 est égal à p3." : "p1 n'est pas égal à p3.");

Résultat :

p1 n'est pas égal à p2.
p1 est égal à p3.

En effet, les références p1 et p2 désignent chacune une instance différente de la classe Person. Ces deux instances sont identiques du point de vue de leurs valeurs, mais elles sont bien distinctes. Ainsi, si je modifie la deuxième instance, cela ne va pas affecter la première :

Console.WriteLine(p1.Age);
Console.WriteLine(p2.Age);
p2.Age = 5;
Console.WriteLine(p1.Age);
Console.WriteLine(p2.Age);

Ce code affichera :

10
10
10
5

Par contre, les références p1 et p3 sont identiques et désignent donc la même instance. Si je modifie p3, cela affecte donc p1 :

Console.WriteLine(p1.Age);
Console.WriteLine(p3.Age);
p3.Age = 42;
Console.WriteLine(p1.Age);
Console.WriteLine(p3.Age);

Ce code affichera :

10
10
42
42

Des méthodes importantes

Equals

Pour reprendre ce qui vient d'être dit, plutôt que d'utiliser l'opérateur ==, vous pouvez utiliser la méthode Equals (que possède tout objet). La seule différence est que == ne compare que des objets de même type, alors que Equals permet de comparer un objet d'un certain type avec un autre objet d'un autre type.

Quel est l'intérêt de Equals, sachant que de toute façon si deux variables ne sont pas du même type, elles ne peuvent pas être égales ?

Cela sert si, à l'écriture du code, vous ne connaissez pas le type des variables concernées.

ToString

Tout objet possède aussi cette méthode. Par défaut, elle renvoie le type de l'objet en question :

Console.WriteLine(p1.ToString());

Résultat :

ConsoleApplication1.Person

La méthode ToString peut être modifiée pour retourner un autre résultat. Par exemple ici on pourrait vouloir renvoyer le nom de la personne en question. Cela se fait avec le modificateur override, que nous étudierons plus tard.

Vous devez commencer à mieux comprendre ce que veut dire Programmation Orientée Objet.
Tant mieux, c'est le but ! :lol:


Créer un objet Les classes (2/2)

Les classes (2/2)

Utiliser un objet Le modificateur "static"

Vous pensez déjà tout savoir sur les classes ? Détrompez-vous, ce n'est que le commencement.

Le modificateur "static"

Les classes (2/2) Application console : étude du code

Le modificateur static se place avant le nom de la classe ou du membre qu'il affecte. Quand vous créez une classe avec des membres, vous faites un moule à objets : à partir de cette classe on fabrique ce qui s'appelle des instances de la classe.
Par exemple si je crée une classe qui contient une chaîne de caractères en champ, chaque instance de la classe aura sa propre version de cette chaîne de caractères.
Le mot-clef static sert à faire que ce qui suit ne dépende pas d'une instance, mais dépende de la classe elle-même.

Voici un exemple :

public class Person
{
	private static int s_count;
	public static int Count
	{
		get { return s_count; }
	}

	public Person()
	{
		s_count++;
	}
}

J'espère que vous reconnaissez une classe assez simple, avec un léger changement : la présence d'un champ statique s_count que l'on incrémente à chaque appel du constructeur. En fait j'ai fait un compteur qui indique le nombre d'objets créés. Ce compteur ne dépend pas d'une instance en particulier, mais de la classe entière.

Comment y accéder ?

Pour accéder à des champs statiques, il faut écrire : nomDeLaClasse.nomDuChamp. Ici, il faut écrire Person.Count pour accéder à s_count qui est mis en private.

On peut aussi utiliser ce modificateur pour une méthode :

public class Person
{
	private static int s_count;
	public static int Count
	{
		get { return s_count; }
	}
	
	public Person()
	{
		s_count++;
	}

	public static void DisplayCount()
	{
		Console.WriteLine(s_count + " objet(s) a(ont) été créé(s).");
	}
}

Dans ce cas, vous pouvez ensuite écrire Person.DisplayCount();.

Améliorons cette méthode avec des if :

public static void DisplayCount()
{
	if (s_count == 0)
	{
		Console.WriteLine("Aucun objet n'a été créé !");
	}
	else if (s_count == 1)
	{
		Console.WriteLine("Un seul objet a été créé.");
	}
	else if (s_count > 1)
	{
		Console.WriteLine(s_count + " objets ont été créés.");
	}
	else
	{
		Console.WriteLine("Erreur : s_count est négatif ! Il vaut : " + s_count);
	}
}

Au cas où tous ces Console.WriteLine ne vous auraient pas fait tilt, eh oui, il s'agit bien de l'appel à la méthode statique WriteLine de la classe Console.

Vous pouvez créer une classe entièrement statique, dont le but n'est alors pas de servir de moule à instances. C'est d'ailleurs le cas de la classe Console : on ne veut pas créer d'objet de type Console, mais faire des choses avec celle qui est ouverte. C'est aussi le cas de la classe Convert qui sert à faire des conversions et que nous verrons sous peu. Pour faire cela il faut mettre static devant le nom de la classe, et du coup devant chacun de ses membres :

public static class MyStaticClass
{
	public static int s_myStaticInt;

	public static string TellSomething()
	{
		return "This is all static!";
	}
}

Rien de bien méchant dans celle-là non plus. Je pense que vous avez saisi l'idée.


Les classes (2/2) Application console : étude du code

Application console : étude du code

Le modificateur "static" Les tableaux

Je vous ai dit comment utiliser la console, sans vraiment fournir d'explications. Nous allons donc à présent étudier le code qui se trouve dans une application console fraîchement créée.

Les espaces de noms

Rappels

Un espace de noms contient des classes (avec des membres : champs, méthodes, etc.) ainsi que d'autres espaces de noms.
Ici, nous avons une classe Program au sein de l'espace de noms ConsoleApplication1 (le nom de mon projet, et aussi de ma solution en l'occurrence).
Un espace de noms se comporte à peu près comme un dossier : il peut contenir des fichiers (ici les classes) et d'autres dossiers (les espaces de noms inclus dedans). Pour accéder au contenu d'un espace de noms, il faut mettre un point après son nom : pour accéder à la classe Program, il faut ici écrire ConsoleApplication1.Program.

Le mot-clef using

Les espaces de noms peuvent "s'emboîter" à l'infinie, ce qui fait que l'écriture peut être longue : namespace1.namespace2.namespace3.namespace4 ...
On utilise donc le mot-clef using.

Les premières lignes débutent par le mot-clef using, sous la forme d'une directive. En écrivant using System; vous informez Visual Studio que s'il ne trouve pas l'espace de noms ou la classe xxx, il devra regarder s'il/elle n'est pas dans System.xxx.
using peut aussi servir à créer un alias qui référence un espace de noms. Par exemple si nous écrivons using Utils = Company.Application.Utilities;, alors nous pourrons écrire Utils.xxx plutôt que d'écrire Company.Application.Utilities.xxx.

D'autre part, using peut être utilisé sous forme de déclaration. Cela permet d'appeler automatiquement la méthode Dispose() de l'objet spécifié. L'objet doit implémenter l'interface IDisposable. Je ne veux pas plus vous embrouiller car nous n'avons pas encore vu les interfaces, donc regardez juste cet exemple :

using (System.IO.StreamReader reader = new StreamReader("readme.txt"))
{
	// Lecture du fichier
}
// Le fichier est automatiquement fermé (dans la méthode Dispose de l'objet nommé reader) après le bloc.

La classe Program

Cette classe a été automatiquement créée par Visual Studio. Elle contient une seule méthode, nommée "Main", qui prend en entrée un tableau de chaînes de caractères et ne retourne rien (void).
static indique que la méthode est... statique ! :p

Un programme console s'appelle depuis la console en passant des paramètres. Ces derniers sont envoyés au programme dans le tableau args.
Le programme commence à s'exécuter dans la méthode Main : c'est le point d'entrée du programme. C'est donc là que nous allons écrire notre code.

Nous n'en avons pas fini avec les classes, mais vous avez le principal : ça suffit pour le moment.


Le modificateur "static" Les tableaux

Les tableaux

Application console : étude du code Tableaux simples

Maintenant que vous savez ce qu'est un objet et que vous êtes initiés à la notion de référence, je peux vous parler des tableaux. :)

Tableaux simples

Les tableaux Tableaux à deux dimensions

Un tableau ("array" en anglais) est un objet permettant de rassembler sous un même identificateur des données de même type. Sa déclaration est la suivante : type[] nom = new type[n];n est le nombre de données que peut contenir le tableau.
En fait, un tableau est même un objet, c'est-à-dire qu'on y accède via une référence. De ce fait, un tableau déclaré sans être initialisé vaut null.

Voici comment créer un tableau de quatre entiers :

int[] myArray = new int[4];

Vous pouvez très bien préciser les valeurs que vous voulez mettre dans votre tableau. Voici comment créer un tableau contenant les entiers 10, 20, 30 et 40 :

int[] myArray = new int[] {10, 20, 30, 40};

Pour simplifier, écrivez :

int[] myArray = {10, 20, 30, 40};

nom[i] désigne la donnée noi, où i appartient à l'intervalle [0; n-1]. Si i n'appartient pas à cet intervalle, ce code générera une erreur. Ainsi, dans cet exemple :


Les tableaux Tableaux à deux dimensions

Tableaux à deux dimensions

Tableaux simples Tableaux de tableaux

Un tableau à deux dimensions se déclare de la manière suivante : type[,] nom = new type[n, m];n est le nombre de lignes et m le nombre de colonnes.
Voici comment déclarer un tableau à deux dimensions contenant des réels :

double[,] myArray = new double[,]
{
	{0.1, 0.5},
	{1.3, 1.7}
};

Pour simplifier, écrivez :

double[,] myArray =
{
	{0.1, 0.5},
	{1.3, 1.7}
};

nom[i, j] désigne l'élément j de la ligne i du tableau nom. Ainsi, dans cet exemple :


Tableaux simples Tableaux de tableaux

Tableaux de tableaux

Tableaux à deux dimensions Effectuer des conversions

Un tableau de tableaux se déclare de la manière suivante : type[][] nom = new type[n][];n est le nombre de lignes.

Allez, je ne vous fais pas plus attendre :

int[][] myIntArray = new int[2][];
myIntArray[0] = new int[5];
myIntArray[0][1] = 42;

Pour bien que vous compreniez, voici l'arborescence de ce tableau de tableaux :

Arborescence du tableau de tableaux d'entiers

Arborescence du tableau de tableaux.

Vous pouvez aussi créer un tableau à partir de variables représentant des tableaux. Voici un exemple pour créer un tableau de tableaux qui contiennent des chaînes de caractères :

string[] firstnameArray =
{
	"Matt",
	"Tim",
	"James"
};

string[] surnameArray =
{
	"Johnson",
	"Smith"
};

string[][] myArray =
{
	firstnameArray,
	surnameArray
};

Chaque élément myArray[i] est une référence de tableau à une dimension. Si vous ne faites que déclarer le tableau, ces références ne sont pas initialisées et elles valent null. C'est le cas dans le code suivant :

int[][] myArrayOfIntArrays = new int[5][];

Après exécution, myArrayOfIntArrays[0] vaut null (de même pour les autres éléments).

Les tableaux sont utiles mais je ne pense pas que nous nous en servirons beaucoup dans ce cours. Dans la partie II, nous verrons une alternative aux tableaux : les collections.


Tableaux à deux dimensions Effectuer des conversions

Effectuer des conversions

Tableaux de tableaux Conversions implicites

Une fois déclarée, une variable ne peut plus être redéclarée ou utilisée pour stocker des valeurs d'un autre type. Sauf si ce type peut être converti au type de la variable en question.
Par exemple, vu qu'il n'est pas possible de convertir une chaîne de caractères en un entier, le code suivant ne compilera pas :

// Erreur : "Cannot implicitly convert type 'string' to 'int'".
int i = "Hello";

Les conversions servent à traiter des données de même type, dans de nombreux cas comme une affectation, et quelque autre opération.
Par exemple, au lieu de manipuler un chiffre, je peux vouloir manipuler le caractère qui lui correspond ; je peux préférer utiliser des entiers plutôt que des nombres à virgule ; ...

En d'autres termes, c'est un point incontournable du C# que je m'apprête à vous expliquer.

En C#, vous pouvez effectuer les types de conversions suivants :

Conversions implicites

Effectuer des conversions Conversions explicites (casts)

Avec des nombres, une conversion implicite peut être effectuée lorsque la valeur à stocker peut tenir dans la variable sans être tronquée ni arrondie. Par exemple, une variable de type long (entier codé sur 8 octets) peut stocker toute valeur qu'un entier (codé sur 4 octets dans un ordinateur 32 bits) peut stocker.
Dans l'exemple suivant, le compilateur convertit implicitement la valeur à droite en un type long avant de l'assigner à bigNum :

int num = 2147483647;
long bigNum = num;

Voici une liste complète de toutes les conversions numériques implicites :

De

Vers

sbyte

short, int, long, float, double ou decimal

byte

short, ushort, int, uint, long, ulong, float, double ou decimal

short

int, long, float, double ou decimal

ushort

int, uint, long, ulong, float, double ou decimal

int

long, float, double ou decimal

uint

long, ulong, float, double ou decimal

long

float, double ou decimal

ulong

float, double ou decimal

char

ushort, int, uint, long, ulong, float, double ou decimal

float

double

Pour les types référence, il existe toujours une conversion implicite entre une classe et l'une des ses classes de base. Aucune syntaxe spéciale n'est nécessaire parce qu'une classe dérivée contient toujours toutes les caractéristiques de ses classes de base. Exemple :

Derived d = new Derived();
Base b = d;

Remarque

J'imagine que je vais en surprendre certains si je vous dis que le code suivant est correct : string s = "M@teo" + 21;. En fait, ce code est équivalant à celui-ci : string s = string.Concat("M@teo", 21);. Et quand je dis équivalant, le mot est faible ! Figurez-vous que le compilateur transforme automatiquement le premier code en le deuxième. À l'exécution, c'est toujours Concat qui est appelée.

La surcharge de Concat que nous utilisons prend des objets de type object et non string en paramètres, donc 21 est converti en type object (le type de base de tout objet). Il ne s'agit donc pas d'une conversion implicite entre types numériques mais entre type de base et type dérivé. Pour retourner une chaîne de caractères, Concat appelle la méthode ToString implémentée par tout objet. Par défaut, cette dernière retourne une chaîne de caractères représentant le type de l'objet. Pour le type int, ToString a été modifiée (avec le modificateur override que nous verrons plus tard) et retourne l'entier considéré sous forme de chaîne de caractères.

Si 21 est passé en paramètre avec le type object, pourquoi est-ce que c'est la méthode int.ToString qui est appelée et non pas object.ToString ?

Très bonne question. :D Nous verrons ça plus tard, quand nous parlerons de polymorphisme.


Effectuer des conversions Conversions explicites (casts)

Conversions explicites (casts)

Conversions implicites Classes et méthodes de conversion

Les casts

Si une conversion ne peut pas être faite sans risque de perte d'informations, le compilateur requiert que vous effectuiez une conversion explicite, appelée cast. Un cast est une façon d'informer explicitement le compilateur que vous projetez de faire la conversion et que vous êtes informés qu'une perte de données peut se produire. Pour effectuer un cast, spécifiez le type voulu entre parenthèses devant la valeur ou la variable à convertir.

float myFloat = 10.605F;
int myInt = (int)myFloat;

Au cas où vous auriez oublié, le F signifie que le nombre est un float. float myFloat = 10.056; ne compilerait pas. En effet, le compilateur comprend 10.056 comme un double, le F est là pour lui préciser que c'est un float (un f minuscule convient tout aussi).

Après exécution, myFloat vaut 10,605 et myInt vaut la partie entière de myFloat, soit 10.

Voici la liste des conversions numériques explicites autorisées :

De

Vers

sbyte

byte, ushort, uint, ulong ou char

byte

sbyte ou char

short

sbyte, byte, ushort, uint, ulong ou char

ushort

sbyte, byte, short ou char

int

sbyte, byte, short, ushort, uint, ulong, ou char

uint

sbyte, byte, short, ushort, int ou char

long

sbyte, byte, short, ushort, int, uint, ulong ou char

ulong

sbyte, byte, short, ushort, int, uint, long ou char

char

sbyte, byte ou short

float

sbyte, byte, short, ushort, int, uint, long, ulong, char, ou decimal

double

sbyte, byte, short, ushort, int, uint, long, ulong, char, float, ou decimal

decimal

sbyte, byte, short, ushort, int, uint, long, ulong, char, float ou double

Pour les types référence, les choses sont plus compliquées et nous verrons cela plus tard.


Conversions implicites Classes et méthodes de conversion

Classes et méthodes de conversion

Conversions explicites (casts) Manipuler les chaînes de caractères

Les Classes Convert et TypeConverter

La classe statique Convert, de l'espace de noms System, implémente des méthodes qui permettent de convertir des variables d'un type vers un autre type.

Les types de base pris en charge sont Boolean (bool), Char (char), SByte (sbyte), Byte (byte), Int16 (short), Int32 (int), Int64 (long), UInt16 (ushort), UInt32 (uint), UInt64 (ulong), Single (float), Double (double), Decimal (decimal), DateTime et String (string).

Il existe une méthode de conversion pour convertir chaque type de base en chaque autre type de base. À l'exécution, quatre résultats sont possibles :

Aucune exception n'est levée si la conversion d'un type numérique entraîne une perte de précision (c'est-à-dire la perte de quelques chiffres moins significatifs). Cependant, une exception est levée si le résultat ne peut pas "tenir" dans le type voulu.

Par exemple, lorsqu'un Double est converti en un Single (float), cela peut entraîner une perte de précision, mais aucune exception n'est levée. Cependant, si la valeur du Double est trop élevée pour être représentée par un Single, une exception de dépassement est levée.

Vous apprendrez davantage de choses sur la classe Convert par la suite, et je parlerai de la classe TypeConverter une fois que vous saurez ce qu'est une interface.

Méthodes

D'autre part, il existe des méthodes qui ne sont pas dans des classes spéciales mais qui sont directement accessibles pour des types de base.
Par exemple, la structure Int32 (int) implémente une méthode Parse (surchargée) pour effectuer une conversion vers un entier. Cependant, si la conversion ne réussit pas, un exception est levée. Vous pouvez ensuite gérer l'exception comme bon vous semble mais nous verrons cela dans la partie II seulement.
Sinon il est intéressant d'utiliser la méthode TryParse de cette même structure (int) pour s'assurer qu'aucune exception ne sera levée. Cette méthode prend divers paramètres, dont un entier, précédé de out. Si vous ne vous rappelez plus à quoi sert ce out, je vous invite à relire le paragraphe sur les passages par référence, dans la sous-partie "Appeler une méthode" du chapitre "Les méthodes".
Place aux exemples (l'exemple no3 n'est là qu'à titre illustratif puisque nous n'avons pas encore étudié les exceptions) :

int parsedInt;
if (int.TryParse("1234", out parsedInt))
{
	Console.WriteLine("Conversion réussie.");
}
else
{
	Console.WriteLine("Conversion impossible.");
}
int parsedInt;
int.TryParse("1234", out parsedInt);
int parsedInt;
try
{
	parsedInt = int.Parse("1234");
}
catch (Exception ex)
{
	Console.WriteLine("Une erreur est survenue : " + ex.Message);
}

Ces trois exemples font globalement la même chose : on obtient un entier à partir d'une chaîne de caractères. Certaines différences persistent toutefois.
La méthode TryParse retourne un booléen, c'est pourquoi elle est utilisée avec un if dans l'exemple no1.
Vous pouvez aussi faire comme si elle ne retournait rien en l'utilisant en simple déclaration (voir exemple no2).
Dans l'exemple no3, on essaie directement de parser la chaîne de caractères ; si ce n'est pas possible, on signale l'erreur.

Je sais que nous n'avons pas encore vu les exceptions, et que par conséquent l'exemple no3 ne doit pas vous parler. Ce n'est pas bien grave : cet exemple n'est là qu'à titre illustratif, et n'est pas crucial.

Le C# est un langage dans lequel la rigueur est de mise lorsqu'il s'agit de manipuler des variables de types différents. Certains peuvent trouver cela trop contraignant, mais c'est un bon moyen de comprendre ce qui se passe réellement et cela permet d'éviter des erreurs.


Conversions explicites (casts) Manipuler les chaînes de caractères

Manipuler les chaînes de caractères

Classes et méthodes de conversion La concaténation

Il existe différentes manières de mettre à la suite des chaînes de caractères, à l'aide :

La concaténation

Manipuler les chaînes de caractères Les chaînes de formatage

Concaténer signifie mettre bout à bout des chaînes de caractères.

L'opérateur +

En C#, vous pouvez utiliser l'opérateur +. Ce n'est pas bien compliqué : il suffit de séparer des chaînes de caractères avec un + :

string firstString = "Hello";
string secondString = "World!";

string fullString = firstString + " " + secondString;

Vous voyez que vous pouvez directement mettre des chaînes de caractères "en dur" entre crochets, comme ici avec l'espace qui sépare les deux mots.

La méthode Concat

La méthode Concat de la classe String (aussi accessible via l'alias string) permet de mettre bout à bout les paramètres qui lui sont passés (en essayant de les convertir en chaînes de caractères).
Cela revient à séparer les chaînes de caractères par un +, ainsi string s1 = string.Concat("Hello ", "World!"); revient à faire string s1 = "Hello " + "World!";.


Manipuler les chaînes de caractères Les chaînes de formatage

Les chaînes de formatage

La concaténation La classe StringBuilder

Une chaîne de formatage est une chaîne de caractères avec des caractères spéciaux, reconnus et remplacés par la représentation d'objets sous forme de chaînes de caractères.
C'est notamment ce que nous utilisons avec la méthode Console.WriteLine :

404 Image not found

La méthode WriteLine vue depuis l'IntelliSense.
Image 010.080.20000

Lorsque vous appelez une fonction qui prend une chaîne de caractères en premier paramètre et ensuite un tableau d'objet précédé de params, c'est qu'on attend une chaîne de formatage (d'ailleurs dans ce cas le nom du paramètre de type string est "format").

En général, ces fonctions sont surchargées, ne vous affolez donc pas si vous ne voyez pas tout de suite les mêmes paramètres, et parcourez les différentes surcharges. Voici les quelques surcharges de la méthode Console.WriteLine :

404 Image not found

Surcharges de la méthode WriteLine.
Image 010.080.20100

Dans une chaîne de formatage, {i} est remplacé par la représentation sous forme de chaîne de caractères du paramètre no(i+2) (le paramètre no1 est une chaîne de formatage).
L'index i doit appartenir à 0 inclus et (n-2) inclus : [0; n-2] ; sinon une exception est levée.
Si vous voulez écrire {i} sans que cela soit reconnu comme des caractères spéciaux, doublez les accolades : écrivez {{i}}.

Comme vous le savez normalement, Console.WriteLine("5 + 6 = {0}", 5+6); affichera :

5 + 6 = 11

Cependant, vous pouvez aussi utiliser les chaînes de formatage, même quand on n'attend qu'une simple chaîne de caractères. Pour ce faire, utilisez la méthode String.Format qui retourne la chaîne de caractères que vous voulez construire à partir de divers éléments.

404 Image not found

Surcharges de la méthode Format.
Image 010.080.20200

On retrouve globalement les mêmes surcharges.
Allez, un petit exemple :

string s = String.Format(
	"{{0}} est remplacé par {0} et {{1}} par {1}",
	"paramètre2",
	"paramètre3");

Après exécution, s vaut "{0} est remplacé par paramètre2 et {1} par paramètre3".

Une dernière chose à savoir avant de laisser les chaînes de formatage : vous pouvez déterminer la façon dont une donnée va être représentée en faisant suivre l'index i de deux points et de caractères spéciaux, comme ceci : {0:X}.

Voici donc les différents caractères spéciaux, ainsi que des exemples :

Caractère

Description

Exemples

Sortie (en-US)

Sortie (fr-FR)

C ou c

Devise

string.Format("{0:C}", 2.5);
string.Format("{0:C}", -2.5);

$2.50
($2.50)

2,50 €
-2.50 €

D ou d

Décimal

string.Format("{0:D5}", 25);

00025

00025

E ou e

Scientifique

string.Format("{0:E}", 250000);

2.500000E+005

2,500000E+005

F ou f

Virgule fixe

string.Format("{0:F2}", 25);
string.Format("{0:F0}", 25);

25.00
25

25,00
25

G ou g

Général

string.Format("{0:G}", 2.5);

2.5

2,5

N ou n

Nombre

string.Format("{0:N}", 2500000);

2,500,000.00

2 500 000,00

X ou x

Hexadécimal

string.Format("{0:X}", 250);
string.Format("{0:X}", 0xffff);

FA
FFFF

FA
FFFF

Que veulent dire en-US et fr-FR ? :euh:

Il s'agit d'identifiants pour désigner des cultures ; en-US désigne "English - United States" et fr-FR désigne "français - France". Dans un programme cela sert à gérer différemment l'affichage suivant l'endroit où vous vivez. La culture dépend de la langue, des habitudes de représentation des nombres, ...

Aucune ligne de code du tableau ne fait mention à la culture ; en fait si vous ne spécifiez rien, votre système d'exploitation s'en charge. Si vous vivez en France, votre culture est par défaut fr-FR, donc vous aurez les résultats de la colonne fr-FR et non en-US.

On peut aussi passer un paramètre de type IFormatProvider. En fait IFormatProvider est une interface, vous devez donc passer un objet dont le type implémente cette interface, comme ici le type CultureInfo.
Je ne veux pas que nous nous attardions trop longtemps sur ce point qui sera étudié par la suite, et est sûrement difficile quand on débute. Je vous donne donc un exemple si ça vous vous intéresse, mais je ne détaille pas à fond :

string s1 = String.Format(
	new System.Globalization.CultureInfo("fr-FR"),
	"{0:C}",
	2.5);

Après exécution, s1 vaut "2,50€".

La méthode ToString prend aussi une chaîne de formatage en paramètre, et là par contre il n'a plus de {i} mais simplement les caractères spéciaux (en effet, en sait déjà ce qu'on doit afficher : c'est l'objet avec lequel on appelle ToString) :

string s2 = 5.ToString(
	"c",
	new System.Globalization.CultureInfo("fr-FR"));

Après exécution, s2 vaut "5,00€".


La concaténation La classe StringBuilder

La classe StringBuilder

Les chaînes de formatage Nouveautés sur la console et les opérateurs

Un objet de type StringBuilder (contenue dans l'espace de noms System.Text) sert à construire petit à petit une chaîne de caractères en optimisant la mémoire.

Les deux méthodes à retenir sont Append qui ajoute une chaîne de caractères à la suite des autres, et ToString qui sert à obtenir la chaîne de caractères complète. Exemple :

string name = "Matt";

StringBuilder sb = new StringBuilder();
sb.Append("Hello ");
sb.Append(name);
sb.Append(", how are you?");

string helloSentence = sb.ToString();

Après exécution, helloSentence vaut "Hello Matt, how are you?".
En fait, l'exemple précédent n'est pas très bon ; il est préférable d'écrire :

string name = "Matt";
string helloSentence = "Hello " + name + ", how are you?";

Pourquoi nous donnes-tu un mauvais exemple ?

Je vous ai donné ce code pour vous montrer la philosophie des objets de type StringBuiler. Le code n'est pas faux, mais le deuxième est plus lisible pour le même résultat.
L'utilisation de StringBuilder permet d'optimiser la mémoire, mais seulement si vous faites des concaténations dans des boucles. Dans le cas qui suit, il s'avère très utile :

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
	sb.AppendLine(name);
}
string fullString = sb.ToString();

Ici j'utilise AppendLine ; c'est comme Append mais ça rajoute un retour à la ligne après.

Voici un bon article à ce sujet.

Après tout, "la programmation est l'art de passer des semaines à gagner quelques millisecondes"...

Le plus important dans ce chapitre est certainement la notion de chaînes de formatage. Cela nous servira notamment lorsque nous travaillerons avec le temps, mais c'est loin d'être la seule utilisation.


Les chaînes de formatage Nouveautés sur la console et les opérateurs

Nouveautés sur la console et les opérateurs

La classe StringBuilder Envoyer des arguments à un programme qui démarre

Pour l'instant, nous n'avons fait qu'afficher du texte en console, mais rassurez-vous : il est possible de faire bien plus. Dans ce chapitre, vous apprendrez à envoyer, via la console, des arguments au démarrage de votre programme.

Je vous présenterai aussi deux nouveaux opérateurs : & et |.

Envoyer des arguments à un programme qui démarre

Nouveautés sur la console et les opérateurs Interagir avec la console

Les arguments arrivent sous forme d'un tableau contenant des chaînes de caractères.

Comment fait-on pour passer des arguments à un programme ?

J'y arrive. Dans la console, après avoir écrit le nom de votre solution suivi de ".exe", laissez un espace et écrivez du texte. Chaque mot est considéré comme un argument ; les espaces font office de séparateur d'arguments. Si toutefois vous souhaitez passer un argument qui contient des espaces, mettez cet argument entre guillemets : "un seul argument avec des espaces" (guillemets inclus) ne constitue qu'un seul argument (dont la valeur est privée des guillemets).

En général un tableau contient une propriété Length qui est un entier valant la taille de ce tableau. Modifiez le code comme ceci dans Visual Studio :

// S'il n'y a pas de 1er argument, on crée une chaîne vide : string.Empty (ou encore "").
string arg1 = (args.Length <= 0) ? string.Empty : args[0];
// Idem pour le 2e
string arg2 = (args.Length <= 1) ? string.Empty : args[1];

// On affiche le 1er argument
Console.WriteLine("argument n°1 : {0}", arg1);
// Idem pour le 2e
Console.WriteLine("argument n°2 : {0}", arg2);

N'oubliez pas de compiler votre programme ! Pour ce faire, appuyez sur F6 pour simplement le mettre à jour, ou sur F5 pour lancer en plus le mode debug. Écrivez ceci dans la console (après le nom du programme) : argument1 argument2.

Et là, magie ! :magicien: Vous voyez ceci :

404 Image not found

Envoi d'arguments à un programme lancé en console.
Image 020.010.40000


Nouveautés sur la console et les opérateurs Interagir avec la console

Interagir avec la console

Envoyer des arguments à un programme qui démarre Évaluations paresseuses des opérateurs && et ||

Afficher du texte en console, c'est bien mais on peut faire encore plus : lire des données en console.
C'est ce que je vais vous apprendre à faire tout de suite.

Il suffit juste de faire string input = Console.ReadLine(); : après exécution de cette instruction, la console attend que vous entriez du texte et dès que vous appuyez sur Entrée, la ligne écrite en console est stockée dans une chaîne de caractères nommée input. Essayez :

// On lit la ligne (la console attend un retour à la ligne avant de mémoriser ce que vous écrivez).
string input = Console.ReadLine();

// On affiche ce qu'on a écrit.
Console.WriteLine("Vous avez écrit : \"{0}\"", input);

Vous pouvez aussi ne récupérer qu'un caractère avec Console.Read();. Cette méthode renvoie un entier représentant le premier caractère entré, il va donc falloir le caster : char myChar = (char)Console.Read();.

Ainsi si je tape abc puis que j'appuie sur Entrée, myChar vaudra 'a'.


Envoyer des arguments à un programme qui démarre Évaluations paresseuses des opérateurs && et ||

Évaluations paresseuses des opérateurs && et ||

Interagir avec la console TP : Plus ou moins

Maintenant que nous avons vu les méthodes, les classes et la console, je me dois de vous faire une remarque sur les opérateurs, en particulier && et ||.

On dit que les évaluations des opérateurs && et || sont paresseuses car elles se font de gauche à droite et elles s'arrêtent dès que le résultat de l'expression entière est connu, ce qui peut arriver sans que l'on ait besoin de tout évaluer.

Par exemple, étant donné que false && xxx ne peut valoir que false, l'expression xxx ne sera pas évaluée. xxx représente ici du code qui est évalué comme un booléen, cela peut donc être un appel à une méthode qui retourne un booléen. Ici, la conséquence est que cette méthode n'est pas appelée, car on n'a pas besoin de son résultat pour évaluer l'expression entière.

De même, true || xxx ne peut valoir que true, donc là non plus l'expression xxx ne sera pas évaluée.

Illustration :

class Program
{
	public static bool Test1()
	{
		Console.WriteLine("Test1");
		return false;
	}

	public static bool Test2()
	{
		Console.WriteLine("Test2");
		return true;
	}

	public static bool Test3()
	{
		Console.WriteLine("Test3");
		return true;
	}

	public static bool Test4()
	{
		Console.WriteLine("Test4");
		return false;
	}

	static void Main(string[] args)
	{
		if (Test1() && Test2())
		{
			Console.WriteLine("Opérateur utilisé : &&");
		}

		if (Test3() || Test4())
		{
			Console.WriteLine("Opérateur utilisé : ||");
		}
	}
}

Après exécution, vous obtenez ceci :

Test1
Test3
Opérateur utilisé : ||

Vous voyez que seules les méthodes Test1 et Test3 ont été appelées. Ils existe une version "non paresseuse" de && et ||, il s'agit de & et |. Modifiez votre méthode Main comme ceci :

static void Main(string[] args)
{
	if (Test1() & Test2())
	{
		Console.WriteLine("Opérateur utilisé : &");
	}

	if (Test3() | Test4())
	{
		Console.WriteLine("Opérateur utilisé : |");
	}
}

Surprise ! Après exécution, vous obtenez ceci :

Test1
Test2
Test3
Test4
Opérateur utilisé : |

Dans ce cas, toutes les méthodes ont été appelées.

Vous connaissez désormais trois fonctionnalités principales de la console : afficher du texte, envoyer des arguments à un programme quand on le démarre et lire le texte entré par l'utilisateur.


Interagir avec la console TP : Plus ou moins

TP : Plus ou moins

Évaluations paresseuses des opérateurs && et || Le jeu

C'est l'heure du TP ! :) Un TP est un bon moyen de voir comment vous vous en sortez tous seuls, et c'est aussi l'occasion pour vous de pratiquer.

Vous allez créer un mini-jeu sur console. Je vais vous dire ce en quoi consistera le jeu, vous donner des conseils, et enfin la solution.

Vous devez chercher par vous-mêmes et vous confronter aux problèmes.

Le jeu

TP : Plus ou moins Correction

But

Vous connaissez sans doute ce jeu, il s'agit du "Plus ou moins" (clin d'œil au passage au tutoriel de M@teo21 sur le langage C dans lequel ce jeu est aussi utilisé ;) ).

Laissez-moi vous rappeler les règles :
Un nombre est tiré entre 0 et 100 (les deux inclus : [0; 100] pour les matheux :p ).
On demande au joueur d'entrer un nombre.
Si ce n'est pas un nombre entre 0 et 100, on indique qu'il y a une erreur.
Sinon, on compare avec le nombre tiré au sort :

Informations indispensables

System.Random rand = new System.Random(); permet de créer un objet à partir de la classe Random, appartenant à l'espace de noms System.

rand étant l'objet créé ci-dessus, et maxValue étant un entier, rand.Next(maxValue) retourne un entier appartenant à [0; maxValue[ (maxValue est une borne exclue).

str étant une chaîne de caractères et integer étant un entier, int.TryParse(str, out integer) essaie de convertir la chaîne de caractères en un entier et retourne un booléen qui vaut true si la conversion a marché (donc false sinon). Revoyez ce que veut dire out si vous avez oublié !

Allez, ça suffit comme ça ; je vous ai déjà mâché tout le travail. Au boulot maintenant, faites bosser vos méninges ! :pirate:


TP : Plus ou moins Correction

Correction

Le jeu Les WinForms (ou Windows Forms)

Voici le code :

static void Main(string[] args)
{
	bool over = false;
	Random rand = new Random();
	int secretNumber = rand.Next(101);

	Console.WriteLine("Bienvenue dans le jeu du Plus ou Moins !");
	Console.WriteLine("Veuillez entrer un nombre compris entre 1 et 100 (inclus).");
	
	// Tant que le jeu n'est pas fini (game over, vous savez !)...
	while (!over)
	{
		string inputString = Console.ReadLine();
		int inputNumber;
		
		// Si la conversion a marché...
		if (int.TryParse(inputString, out inputNumber))
		{
			// Si c'est en dehors des limites...
			if (inputNumber < 0
			    || inputNumber > 100)
			{
				Console.WriteLine("Veuillez entrer un nombre compris entre 1 et 100 (inclus).");
			}
			
			// Si c'est le bon...
			else if (inputNumber == secretNumber)
			{
				Console.WriteLine("Vous avez gagné ! Le nombre mystère était : {0}.", secretNumber);
				over = true;
			}
			
			// Si c'est trop petit...
			else if (inputNumber < secretNumber)
			{
				Console.WriteLine("C'est plus.");
			}
			
			// Si c'est trop grand...
			else
			{
				Console.WriteLine("C'est moins.");
			}
		}
		// Si la conversion n'a pas réussi...
		else
		{
			Console.WriteLine("Une erreur est survenue lors de la conversion.");
			Console.WriteLine("Veuillez entrer un nombre compris entre 1 et 100 (inclus).");
		}
	}
	
	// L'instruction suivante permet de marquer une pause à la fin : le programme attend que vous appuyez sur Entrée et va récupérer ce que vous avez écrit.
	// Ici, le résultat de la fonction n'est pas utilisé : on n'affecte aucune variable avec.
	// On utilise donc la fonction comme si elle rendait void.
	Console.ReadLine();

}

while (!over) revient à faire while (over != true) ou encore while (over == false).

À chaque passage dans la boucle, je crée une string inputString et un int inputNumber. Ces deux variables étant créées dans la boucle, à la fin de chaque tour elles sont détruites, et seront recréées au tour suivant.
Cela fait que ces deux variables ne sont donc accessibles qu'à l'intérieur de la boucle, pas après.

Vous pouvez continuer à créer des mini-jeux sur console si vous voulez.

Nous allons par contre laisser de côté la console à partir de maintenant, pour nous tourner vers les applications en fenêtres (bien plus jolies).

Surprise ! La première partie est finie ! :)

C'était un peu long, mais c'était un passage obligé. Avec tout ce que vous avez appris, vous allez maintenant pouvoir aller plus loin et commencer à faire des choses bien plus avancées et intéressantes. ;)

Allez, on ne perd pas de temps et on enchaîne.


Le jeu Les WinForms (ou Windows Forms)

Les WinForms (ou Windows Forms)

Correction Création de la solution

"WinForms" est le nom de l'interface graphique qui est incluse dans le framework .NET.

C'est ce qui vous permet de faire des applications en fenêtres (appelées forms).

Création de la solution

Les WinForms (ou Windows Forms) Boîte à outils et contrôles

Ouvrez Visual Studio si ce n'est déjà fait.
Cliquez sur "File" > "New Project".
Sélectionnez "Windows Forms Application" :

404 Image not found

Sélection du modèle de projet.
Image 020.030.10000

Du code a automatiquement été généré, notamment :

Program.cs

404 Image not found

Vue d'ensemble du projet fraîchement créé.
Image 020.030.10100

Ça ressemble au code du fichier Program.cs pour une application console, avec tout de même des différences :

Qu'est-ce qu'une Form1 ? :euh:

Pour le savoir, il suffit d'aller jeter un coup d'œil dans Form1.cs. Pour visualiser la partie code, faites un clic-droit et sélectionnez "View Code" :

404 Image not found

Code du fichier Form1.cs.
Image 020.030.10200

Dans le code, vous voyez qu'il y a des nouveaux espaces de noms utilisés : c'est principalement pour l'affichage.
À l'intérieur de notre espace de noms, vous voyez qu'une classe Form1 est déclarée, et qu'elle dérive de la classe Form.

À l'intérieur du constructeur, la méthode InitializeComponent est appelée. Elle est créée dans une autre partie de la classe Form1 située dans Form1.Designer.cs :

404 Image not found

Code du fichier Form1.Designer.cs.
Image 020.030.10500

Vous voyez un bloc nommé "Windows Forms Designer generated code". J'ai laissé ma souris au-dessus pour que vous voyiez ce qu'il contient (on peut aussi l'ouvrir en cliquant sur le plus à gauche).

Pendant qu'on y est, au-dessus de ce bloc vous voyez des lignes bizarres :

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>

Les lignes qui commencent par trois slashes (barres obliques) sont des lignes de commentaires spéciaux. Ces commentaires sont utilisés pour l'IntelliSense. Vous voyez que les commentaires sont faits avec des balises. Entre deux balises summary vous décrivez brièvement à quoi sert ce qui suit (ce peut être une classe, une méthode, ...) en anglais. S'il s'agit d'une méthode qui prend en entrée des arguments, vous pouvez détailler ce que chacun représente avec des balises param.
Regardez ce qu'affiche l'IntelliSense dans Form1.cs :

404 Image not found

Commentaires affichées par l'IntelliSense.
Image 020.030.10600

On retrouve bien la description de la méthode et du paramètre disposing.
Vous pouvez aller voir les autres balises sur MSDN.


Les WinForms (ou Windows Forms) Boîte à outils et contrôles

Boîte à outils et contrôles

Création de la solution La classe MessageBox

Je vous l'ai déjà dit, mais laissez-moi vous rafraîchir la mémoire : la boîte à outils (située sur le côté gauche, et accessible aussi depuis "View" > "Toolbox") contient des contrôles.

Les contrôles sont ces objets que vous pouvez déposer dans une fenêtre par un simple glisser-déposer ; par exemple un bouton, une image, ...

La partie Design vous montre l'allure de votre fenêtre. Pour la voir, double-cliquez sur Form1.cs dans l'explorateur de la solution, ou faites un clic-droit dessus et vous pouvez faire "View Designer" pour voir la partie Design (de même que "View Code" pour voir le code).

Placez un bouton dans votre Form1 en faisant un glisser-déposer d'un objet Button dans la partie Design de votre Form1. Vous pouvez l'étirer comme vous voulez.

404 Image not found

Mise en place d'un bouton depuis la boîte à outils.
Image 020.030.20000

Propriétés

Cliquez sur le bouton "Properties" dans l'explorateur de la solution pour afficher les propriétés.

404 Image not found

Un clic sur l'icône "Properties" permet d'afficher la fenêtre de propriétés.
Image 020.030.20100

Vous voyez en bas à droite les propriétés de l'objet sélectionné (ici le bouton) :

404 Image not found

Propriétés de notre bouton.
Image 020.030.20200

Faites débuter le nom de votre bouton par "m_" en changeant sa propriété Name (j'ai personnellement opté pour m_myTestButton).
Essayez par vous-mêmes de modifier des propriétés pour voir ce que ça donne, sachant qu'en bas de la fenêtre de propriétés, vous bénéficiez d'une description de la propriété sélectionnée.


Création de la solution La classe MessageBox

La classe MessageBox

Boîte à outils et contrôles Les évènements

La classe MessageBox sert à afficher dans une fenêtre pop-up un message pouvant contenir du texte, des boutons et des symboles donnant des informations et des instructions diverses à l'utilisateur.

C'est une classe statique, vous ne pouvez donc pas créer d'instance de cette classe, mais seulement appeler les méthodes qu'elle propose. La méthode qui nous intéresse est Show. Plus vous lui passez d'arguments, plus votre MessageBox sera complète. Elle est surchargée et possède 21 implémentations. Pour choisir laquelle utiliser, lisez ce que vous propose l'IntelliSense lorsque vous écrivez MessageBox.Show( dans le constructeur de la fenêtre Form1 (utilisez les flèches haut/bas pour parcourir les surcharges), ou écrivez simplement MessageBox, faites un clic-droit dessus, et cliquez sur "Go To Definition". En clair, voici le code que vous avez pour l'instant :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
			MessageBox.Show(
		}
	}
}

Cette méthode retourne un DialogResult qui peut valoir (suivant quel bouton est cliqué dans le pop-up) :

DialogResult est une énumération, donc pour accéder à ses valeurs, faites par exemple DialogResult.OK.

Voici quelques exemples (les principaux) :

404 Image not found

Pop-up avec juste un message.
Image 020.030.30000

// Pop-up avec juste un message.
MessageBox.Show("Du texte");
404 Image not found

Pop-up avec un titre et un message.
Image 020.030.30100

// Le même avec un titre.
MessageBox.Show(
	"Du texte",
	"Titre");
404 Image not found

Pop-up avec un titre, un message et deux bouton, Oui et Non.
Image 020.030.30200

// On ajoute des boutons ; ici Oui et Non.
MessageBox.Show(
	"Du texte",
	"Titre",
	MessageBoxButtons.YesNo);
404 Image not found

Pop-up avec un titre, un message, des boutons et une icône d'erreur.
Image 020.030.30300

// On ajoute une icône : celle d'une erreur. On change aussi les boutons...
MessageBox.Show(
	"Du texte",
	"Titre",
	MessageBoxButtons.RetryCancel,
	MessageBoxIcon.Error);

Nous utilisons deux autres énumérations ici : MessageBoxButtons qui représente différents groupes de boutons, et MessageBoxIcon qui représente différentes icônes.

Vous conviendrez que les WinForms ont quand même plus d'allure que la fenêtre toute noire qu'est la console !

Elles sont maintenant délaissées au profit de WPF, qui permet de faire beaucoup plus de choses au niveau design. Cette technologie allie notamment C# et XAML.

À ce stade, je ne considère pas que vous ayez besoin de connaître WPF, les WinForms permettent quand même de faire des choses intéressantes et esthétiques. Nous verrons WPF dans la partie III qui lui sera d'ailleurs exclusivement réservée. En revanche, je ne détaillerai pas tous les contrôles utilisables avec les WinForms. Je vous invite donc à les découvrir par vous-mêmes dans la boîte à outils.


Boîte à outils et contrôles Les évènements

Les évènements

La classe MessageBox Les gestionnaires d'évènements

On appelle évènement une action particulière qui a lieu. Cela peut être un clic de souris, l'appui d'une touche du clavier, ... Il existe une infinité d'évènements puisque vous pouvez même créer les vôtres. Dans ce chapitre, je souhaite vous apprendre le fonctionnement des évènements et vous montrer des exemples de base.

Les gestionnaires d'évènements

Les évènements Les paramètres passés

But

Ce que l'on veut, c'est effectuer une action lorsqu'un certain évènement est déclenché. Pour cela, il faut déjà savoir quand est déclenché l'évènement dont il est question.
C'est ici qu'entrent en jeu les gestionnaires d'évènements ("event handlers" en anglais). Il servent à indiquer que l'évènement surveillé vient d'être déclenché.

Fonctionnement

Le principe est simple : on attache un gestionnaire d'évènements à un évènement (on dit que l'on s'abonne à l'évènement) en spécifiant une méthode. Cette méthode est appelée lorsque l'évènement est déclenché.

Reprenons notre fameux bouton ; double-cliquez dessus : cela va créer un gestionnaire d'évènements. Voici le code généré (par le double-clic) dans Form1.Designer.cs :

this.m_myTestButton.Click += new System.EventHandler(this.m_myTestButton_Click);

La propriété Click de mon bouton m_myTestButton est un gestionnaire d'évènements, cela signifie que des méthodes vont être appelées par m_myTestButton à chaque fois que je clique dessus, en l'occurrence la méthode appelée est m_myTestButton_Click.

Du code a aussi été généré automatiquement dans Form1.cs :

private void m_myTestButton_Click(object sender, EventArgs e)
{

}

Cela définit une fonction privée, qui ne retourne rien et prend en paramètres deux choses :

Modifions le texte écrit dans notre bouton ! Un objet de type Button contient une propriété appelée Text qui correspond au texte affiché dans ce bouton. Complétez donc la méthode précédente comme suit :

private void m_myTestButton_Click(object sender, EventArgs e)
{
	m_myTestButton.Text = "Bouton cliqué !";
}

Appuyez sur F5 pour compiler la solution et lancer le programme en mode debug. Cliquez sur le bouton et là vous verrez que le texte a changé.

Place aux explications concernant cette ligne :

this.m_myTestButton.Click += new System.EventHandler(this.m_myTestButton_Click);

Si vous laissez la souris sur le mot Click juste derrière this.m_myTestButton., un petit rectangle contenant des descriptions apparaît ; on appelle ça une infobulle (tooltip en anglais). Cette infobulle vous dit que Click est de type System.EventHandler, qu'il est défini dans la classe Control (Button dérive de Control) et on vous dit "Occurs when the control is clicked." ("se produit quand l'utilisateur clique sur le contrôle").
Le += signifie que vous ajoutez une action à effectuer si l'évènement est déclenché. En l'occurrence on ajoute un gestionnaire d'évènements qui appellera la méthode m_myTestButton_Click.
De même, -= signifie que vous enlevez une action à effectuer si l'évènement est déclenché.

Vous pouvez mettre à la suite plusieurs lignes similaires à la précédente ; ici je considère que DoSomething, DoSomethingElse et DoAnotherThing sont des méthodes de la classe Form1 (cf. le "this") :

this.m_myTestButton.Click += new System.EventHandler(this.DoSomething);
this.m_myTestButton.Click -= new System.EventHandler(this.DoSomethingElse);
this.m_myTestButton.Click += new System.EventHandler(this.DoAnotherThing);

Pourquoi avoir utilisé this ici ? :euh:

Nous ne sommes pas obligés. Je les ai simplement mis car ils sont présents dans le code automatiquement généré (dans le fichier Form1.Designer.cs) ; je voulais donc suivre la logique de présentation de code. En revanche, si je devais utiliser les gestionnaires d'évènements pour quelque chose qui n'a rien à voir avec le rendu visuel, je ne les mettrais pas dans Form1.Designer.cs et je n'utiliserais pas this.

Exemples d'évènements

Click est loin d'être le seul évènement de notre bouton. Pour voir tous ses évènements, ouvrez votre Form1 en mode Design et sélectionnez le bouton pour voir ses propriétés dans la fenêtre Properties (assurez-vous que l'éclair de cette fenêtre est sélectionné) :

404 Image not found

Évènements liés au bouton (le bouton avec l'image d'éclair doit être activé).
Image 020.040.10000

Les noms sont intuitifs et comme vous pouvez le constater, il suffit de sélectionner un évènement pour voir sa description en bas. Double-cliquer sur un évènement génère automatiquement le code du gestionnaire d'évènement et de la méthode à appeler. Étudions à présent cette méthode.