Partager cette page

Apprendre X en Y minutes

Où X=c#

C# est un langage de programmation orienté objet à typage fort qui permet aux développeurs de créer une grande variété d'applications fiables et robustes s'appuyant sur le framework .NET.

Plus d'infos

// Les commentaires sur une seule ligne commencent par //
/*
Les
commentaires
multi-lignes
ressemblent
à
ceci
*/
/// <summary>
/// Ceci est un commentaire de documentation XML
/// </summary>

// Importez des namespaces avec l'instruction 'using'
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Threading.Tasks;

// Définit la portée du code pour une meilleure organisation
namespace Learning
{
    // Chaque fichier .cs devrait au moins contenir une classe avec le même nom 
    // que celui du fichier. Ce n'est pas une obligation mais c'est mieux ! 
    public class LearnCSharp
    {
        // LES BASES - si vous avez déjà de l'expérience en Java ou C++
        // passez directement à la partie FONCTIONNALITÉS INTERÉSSANTES 
        public static void Syntax() 
        {
            // Utilisez Console.WriteLine pour écrire sur la sortie
            Console.WriteLine("Hello World");
            Console.WriteLine(
                "Entier: " + 10 +
                " Double: " + 3.14 +
                " Booleen: " + true);

            // Pour omettre le retour à la ligne : Console.Write
            Console.Write("Hello ");
            Console.Write("World");

            ///////////////////////////////////////////////////
            // Types et Variables
            // Déclarez une variable avec la syntaxe <type> <nom>
            ///////////////////////////////////////////////////

            // Sbyte - Entier signé sur 8 bits
            // (-128 <= sbyte <= 127)
            sbyte fooSbyte = 100;

            // Byte - Entier non-signé sur 8 bits
            // (0 <= byte <= 255)
            byte fooByte = 100;

            // Short - Entier sur 16 bits
            // Signé - (-32,768 <= short <= 32,767)
            // Non-signé - (0 <= ushort <= 65,535)
            short fooShort = 10000;
            ushort fooUshort = 10000;

            // Int - Entier sur 32 bits
            int fooInt = 1; // (-2,147,483,648 <= int <= 2,147,483,647)
            uint fooUint = 1; // (0 <= uint <= 4,294,967,295)

            // Long - Entier sur 64 bits
            long fooLong = 100000L; // (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807)
            ulong fooUlong = 100000L; // (0 <= ulong <= 18,446,744,073,709,551,615)
            // Par défaut le type d'un littéral entier est int ou uint
            // on ajoute 'L' pour spécifier la création d'un long

            // Double - Réel sur 64 bits en virgule flottante (norme IEEE 754) 
            double fooDouble = 123.4; // Precision : 15-16 chiffres

            // Float - Réel sur 32 bits en virgule flottante (norme IEEE 754) 
            float fooFloat = 234.5f; // Precision : 7 chiffres
            // Par défaut le type d'un littéral réel est double
            // on ajoute 'f' pour spécifier la création d'un float

            // Decimal - Type de donnée numérique sur 128 bits, fournit une plus 
            // grande précision et une plage de valeurs réduite.
            // Approprié aux calculs financiers et monétaires
            decimal fooDecimal = 150.3m;

            // Booléen - vrai / faux
            bool fooBoolean = true; // ou false

            // Char - Un unique caractère Unicode sur 16 bits
            char fooChar = 'A';

            // String -- contrairement aux types précédents qui sont des types valeurs,
            // string est un type référence. Il peut donc avoir la valeur null
            string fooString = "\"échappement\" de guillemets et ajout de \n (nouvelle ligne) et  de \t (tabulation)";
            Console.WriteLine(fooString);

            // Il est possible d'accéder à chaque caractère d'une chaîne de caractères via son index
            char charFromString = fooString[1]; // 'é'
            // une chaîne de caractères est immuable : impossible de faire fooString[1] = 'X';

            // Comparaison de chaînes de caractères avec la culture courrante en ignorant la casse
            string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase);

            // Formatage
            string fooFs = string.Format("Check Check, {0} {1}, {0} {1:0.0}", 1, 2);

            // Dates et formatage
            DateTime fooDate = DateTime.Now;
            Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy"));

            // Il est possible d'étaler une chaîne de caractères sur plusieurs lignes avec le symbole @.
            // Pour échapper " utilisez ""
            string bazString = @"Voici quelques trucs
sur une nouvelle ligne! ""Wow!"", quel style";

            // Utilisez const ou read-only pour rendre une variable immuable.
            // Les valeurs constantes sont calculées au moment de la compilation
            const int HOURS_I_WORK_PER_WEEK = 9001;

            ///////////////////////////////////////////////////
            // Structures de données
            ///////////////////////////////////////////////////

            // Tableaux - indexé à partir de zéro
            // La taille d'un tableau doit être décidée à la déclaration
            // La syntaxe pour déclarer un tableau est la suivante :
            // <type>[] <nom> = new <type>[<taille>]
            int[] intArray = new int[10];

            // Une autre méthode de déclaration et d'initialisation
            int[] y = { 9000, 1000, 1337 };

            // Indexer un tableau - Accéder à un élément
            Console.WriteLine("intArray à 0: " + intArray[0]);
            // Les tableaux sont muables.
            intArray[1] = 1;

            // Listes
            // Elles sont plus souvent utilisées que les tableaux car plus souples
            // La syntaxe pour déclarer une liste est la suivante :
            // List<type> <nom> = new List<type>();
            List<int> intList = new List<int>();
            List<string> stringList = new List<string>();
            List<int> z = new List<int> { 9000, 1000, 1337 }; // intialisation
            // Les <> indiquent un type générique 
            // Pus d'info dans la partie FONCTIONNALITÉS INTERÉSSANTES

            // Les éléments d'une liste ne sont pas null par défaut
            // Il faut ajouter une valeur avant d'y accéder par index
            intList.Add(1);
            Console.WriteLine("intList à 0: " + intList[0]);

            // Autres structures de données à étudier :
            // Stack/Queue (Pile/File)
            // Dictionary (une implémentation de hash map)
            // HashSet (représente un ensemble)
            // Collections en lecture seule
            // Tuple (.Net 4+)

            ///////////////////////////////////////
            // Opérateurs
            ///////////////////////////////////////
            Console.WriteLine("\n->Opérateurs");

            int i1 = 1, i2 = 2; // Raccourci pour des déclarations multiples

            // Arithmétique classique
            Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3

            // Modulo
            Console.WriteLine("11%3 = " + (11 % 3)); // => 2

            // Opérateurs de comparaison
            Console.WriteLine("3 == 2? " + (3 == 2)); // => False
            Console.WriteLine("3 != 2? " + (3 != 2)); // => True
            Console.WriteLine("3 > 2? " + (3 > 2)); // => True
            Console.WriteLine("3 < 2? " + (3 < 2)); // => False
            Console.WriteLine("2 <= 2? " + (2 <= 2)); // => True
            Console.WriteLine("2 >= 2? " + (2 >= 2)); // => True

            // Opérateurs bit à bit !
            /*
            ~       Compément unaire
            <<      Décalage à gauche
            >>      Décalage à droite
            &       ET logique
            ^       OU exclusif
            |       OU inclusif
            */

            // Incrémentations
            int i = 0;
            Console.WriteLine("\n->Inc/Dec-rementation");
            Console.WriteLine(i++); //i = 1. Post-Incrémentation
            Console.WriteLine(++i); //i = 2. Pre-Incrémentation
            Console.WriteLine(i--); //i = 1. Post-Decrémentation
            Console.WriteLine(--i); //i = 0. Pre-Decrémentation

            ///////////////////////////////////////
            // Structures de contrôle
            ///////////////////////////////////////
            Console.WriteLine("\n->Structures de contrôle");

            // Structure conditionnelle
            int j = 10;
            if (j == 10)
            {
                Console.WriteLine("Je serai affiché");
            }
            else if (j > 10)
            {
                Console.WriteLine("Pas moi");
            }
            else
            {
                Console.WriteLine("Moi non plus");
            }

            // Opérateur ternaire
            // Un simple if/else peut s'écrire :
            // <condition> ? <valeur si true> : <valeur si false>
            int toCompare = 17;
            string isTrue = toCompare == 17 ? "True" : "False";

            // Boucle while
            int fooWhile = 0;
            while (fooWhile < 100)
            {
                // 100 passages, de 0 à 99
                fooWhile++;
            }

            // Boucle Do While
            int fooDoWhile = 0;
            do
            {
                // 100 passages, de 0 à 99
                fooDoWhile++;
            } while (fooDoWhile < 100);

            // Boucle for 
            // Structure : for(<etat_initial>; <condition>; <pas>)
            for (int fooFor = 0; fooFor < 10; fooFor++)
            {
                // 10 passages, de 0 à 9
            }

            // La boucle foreach
            // Structure : foreach(<type_iterateur> <nom_iterateur> in <enumerable>)
            // Cette boucle est utilisable sur des objets implémentant IEnumerable ou IEnumerable<T>
            // Toutes les collections du framework .NET (Tableaux, Listes, ...) implémentent ces interfaces.
            // (Notez que dans l'exemple suivant .ToCharArray() peut être omit car
            //  string implémente IEnumerable)
            foreach (char character in "Hello World".ToCharArray())
            {
                //Itération sur chaque caractère
            }

            // La structure Switch Case
            // Un switch fonctionne avec les types : byte, short, char et int.
            // Les enums sont aussi supportés ainsi que les chaînes de caractères et quelques
            // classes spéciales basées sur les types primitifs : Character, Byte, Short et Integer.
            int mois = 3;
            string moisString;
            switch (mois)
            {
                case 1:
                    moisString = "Janvier";
                    break;
                case 2:
                    moisString = "Février";
                    break;
                case 3:
                    moisString = "Mars";
                    break;

                // Vous pouvez assigner plus d'un 'case' à une action
                // Mais vous ne pouvez pas ajouter une action sans 'break' avant un 'case'
                // (pour ce faire, il faudrait ajouter explicitement un 'goto case x')
                case 6:
                case 7:
                case 8:
                    moisString = "C'est l'été!";
                    break;
                default:
                    moisString = "Un autre mois oO";
                    break;
            }

            ///////////////////////////////////////
            // conversion de type de donnée et transtypage
            ///////////////////////////////////////

            // conversion de string vers int
            // lève une exception en cas d'erreur
            int.Parse("123"); //retourne la valeur entière de "123"

            // TryParse affecte la valeur par défaut du type en cas d'erreur
            // dans ce cas : 0
            int tryInt;
            if (int.TryParse("123", out tryInt)) // La fonction retourne un booléen
                Console.WriteLine(tryInt);       // => 123

            // conversion d'un entier vers une chaîne de caractères
            // La classe Convert possède plusieurs méthodes pour faciliter la conversion
            Convert.ToString(123);
            // ou
            tryInt.ToString();
        }

        ///////////////////////////////////////
        // CLASSES - voir les définitions à la fin du fichier
        ///////////////////////////////////////

        public static void Classes()
        {
            // voir les déclarations à la fin du fichier

            // Utilisez 'new' pour instancier une classe
            Bicycle trek = new Bicycle();

            // Appel des méthodes de l'objet
            trek.SpeedUp(3); // Il est toujours bon d'utiliser des accesseurs
            trek.Cadence = 100;

            // Affichage de la valeur de retour d'une méthode.
            Console.WriteLine("trek info: " + trek.Info());

            // Instanciation d'un nouveau PennyFarthing
            PennyFarthing funbike = new PennyFarthing(1, 10);
            Console.WriteLine("funbike info: " + funbike.Info());

            Console.Read();
        }

        // POINT D'ENTRÉE - Une application console doit avoir une méthode main comme point d'entrée
        public static void Main(string[] args)
        {
            OtherInterestingFeatures();
        }

        //
        // FONCTIONNALITÉS INTÉRÉSSANTES
        //

        // SIGNATURE DE METHODE
        public // Visibilité
        static // Permet un appel direct par la classe (sans instanciation) 
        int // Type de retour,
        MethodSignatures(
            int maxCount, // Premier paramètre, de type int
            int count = 0, // Valeur par défaut si aucun argument n'est passé
            int another = 3,
            params string[] otherParams // Capture tous les arguments passés à la méthode
        )
        { 
            return -1;
        }

        // Des méthodes peuvent avoir le même nom tant que leur signature est unique
        public static void MethodSignature(string maxCount)
        {
        }

        // TYPE GÉNÉRIQUE

        // Les types TKey et TValue sont spécifiés par l'utilisateur lors de l'appel de la fonction
        // Cette méthode émule SetDefaut de Python
        public static TValue SetDefault<TKey, TValue>(
            IDictionary<TKey, TValue> dictionary, 
            TKey key, 
            TValue defaultItem)
        {
            TValue result;
            if (!dictionary.TryGetValue(key, out result))
                return dictionary[key] = defaultItem;
            return result;
        }

        // Vous pouvez limiter les types autorisés
        public static void IterateAndPrint<T>(T toPrint) where T: IEnumerable<int>
        {
            // Nous sommes sûrs de pouvoir itérer, car T implémente IEnumerable<int>
            foreach (var item in toPrint)
                // Item sera de type int
                Console.WriteLine(item.ToString());
        }

        public static void OtherInterestingFeatures()
        {
            // PARAMÈTERES OPTIONNELS
            MethodSignatures(3, 1, 3, "Des", "Paramètres", "En plus");
            MethodSignatures(3, another: 3); // affectation explicite, les autres 
                                             // paramètres ont la valeur par défaut

            // MÉTHODE D'EXTENSION
            int i = 3;
            i.Print(); // Définit plus bas

            // TYPES NULLABLE - idéal pour les interactions avec une base de données ou pour les valeurs de retour
            // Tous les types valeurs peuvent être rendus nullable en les suffixant par '?'
            // <type>? <nom> = <value>
            int? nullable = null; // raccourci pour Nullable<int>
            Console.WriteLine("Nullable variable: " + nullable);
            bool hasValue = nullable.HasValue; // retourne vrai si la valeur n'est pas null

            // ?? est un sucre syntaxique pour spécifier une valeur par défaut
            // au cas ou une autre valeur serait nulle
            int notNullable = nullable ?? 0; // 0

            // VARIABLES IMPLICITEMENT TYPÉES - vous pouvez laisser le compilateur deviner le type d'une variable
            var magic = "magic est de type string à la compilation. On a toujours un typage fort !";
            // magic = 9; // ne fonctionnera pas car magic est désormais une chaîne de caractères

            // TYPES GÉNÉRIQUES
            var agenda = new Dictionary<string, string>() { 
                {"Sarah", "212 555 5555"} // Ajout d'une entrée à notre agenda
            };

            // Appel de la fonction SetDefault (définie plus haut)
            Console.WriteLine(SetDefault<string,string>(agenda, "Shaun", "Pas de numéro")); // => Pas de numéro
            // Notez que vous n'avez pas à spécifier TKey et TValue car le compilateur saura les inférer.
            Console.WriteLine(SetDefault(agenda, "Sarah", "No Phone")); // => 212 555 5555

            // EXPRESSION LAMBDA - permet d'écrire une fonction en tant qu'expression
            Func<int, int> square = (x) => x * x; // La dernière expression est la valeur de retour
            Console.WriteLine(square(3)); // => 9

            // GESTION AUTOMATIQUE DES RESSOURCES - vous permet de manipuler facilement des resources non-managées
            // La plus part des objets qui accèdent à des ressources non-managées (handle de fichier, périphérique, etc.)
            // implémentent l'interface IDisposable. L'instruction using prend soin
            // de libérer les objets IDisposable proprement à votre place.
            using (StreamWriter writer = new StreamWriter("log.txt"))
            {
                writer.WriteLine("Rien à signaler");
                // À la fin de cette portée les ressources seront libérées.
                // Même si une exception est levée.
            } 

            // BIBLIOTHÈQUE DE TÂCHES PARALLÈLES (TPL)
            // http://msdn.microsoft.com/fr-fr/library/dd460717.aspx
            var websites = new string[] { 
                "http://www.google.com", "http://www.reddit.com", 
                "http://www.shaunmccarthy.com"
            };
            var responses = new Dictionary<string, string>();

            // L'exemple suivant exécutera chaque requête dans un thread séparé,
            // et attendra la fin de chacun d'entre eux avant de continuer
            Parallel.ForEach(websites, 
                new ParallelOptions() {MaxDegreeOfParallelism = 3}, // maximum de 3 threads
                website =>
            {
                // Fait quelque chose de long
                using (var r = WebRequest.Create(new Uri(website)).GetResponse())
                {
                    responses[website] = r.ContentType;
                }
            });

            // Ceci ne s'exécutera pas tant que les threads n'auront pas fini leur travail
            foreach (var key in responses.Keys)
                Console.WriteLine("{0}:{1}", key, responses[key]);

            // TYPE DYNAMIQUE - idéal pour travailler avec d'autres langages
            dynamic student = new ExpandoObject();
            student.FirstName = "Mon prénom"; // Pas besoin de définir l'objet

            // Vous pouvez même ajouter des méthodes (dans cet exemple : la méthode prend une chaîne de caractères et retourne une chaîne de caractères)
            student.Introduce = new Func<string, string>(
                (introduceTo) => string.Format("Hey {0}, c'est {1}", student.FirstName, introduceTo));
            Console.WriteLine(student.Introduce("Beth"));

            // IQUERYABLE<T> - quasiment toutes les collections implémentent cette interface
            // ce qui permet d'utiliser des méthodes de style 'Filter' / 'Map' / 'Reduce' 
            var bikes = new List<Bicycle>();
            bikes.Sort(); // Trie le tableau sur place
            bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // Trie en se basant sur la propriété Wheels
            var result = bikes
                .Where(b => b.Wheels > 3) // 'Filter' - enchaînable (retourne un IQueryable du type précédent)
                .Where(b => b.IsBroken && b.HasTassles)
                .Select(b => b.ToString()); // 'Map' - on retourne le .ToString() de chaque élément filtré,
                                            // le résultat est un IQueryable<string>

            var sum = bikes.Sum(b => b.Wheels); // 'Reduce' - fait la somme de tous les Wheels de la liste

            // Creation d'une liste d'objet anonymes basés sur des paramètres de la classe Bike
            var bikeSummaries = bikes.Select(b=>new { Name = b.Name, IsAwesome = !b.IsBroken && b.HasTassles });
            // Le compilateur peut inférer le type de ces objets anonymes, permettant à certains IDE d'effectuer 
            // des autos-complétion.
            foreach (var bikeSummary in bikeSummaries.Where(b => b.IsAwesome))
                Console.WriteLine(bikeSummary.Name);


            // ASPARALLEL
            // C'est ici que les choses se compliquent - un mélange de LINQ et de TPL
            var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name);
            // La ligne précédente s'exécute en parallèle ! Des threads seront gérés automatiquement
            // et les données y seront réparties. Idéal sur de grosses données (et si votre 
            // machine dispose de plusieurs coeurs)


            // LINQ - lie une source de données à des objets IQueryable<T>
            // ex : LindToSql => liaison avec une base de données, LinqToXml => liaison avec un document xml
            var db = new BikeRespository();

            // l'exécution est décalée, ce qui est préférable quand on travaille sur une base données
            var filter = db.Bikes.Where(b => b.HasTassles); // pas de requête exécutée
            if (42 > 6) //  Vous pouvez continuer à affiner la recherche
                filter = filter.Where(b => b.IsBroken); // pas de requête exécutée

            var query = filter
                .OrderBy(b => b.Wheels)
                .ThenBy(b => b.Name)
                .Select(b => b.Name); // toujours pas de requête exécutée

            // Maintenant la requête est exécutée, mais retourne des données uniquement au fil de l'itération
            foreach (string bike in query) 
                Console.WriteLine(result);

        }

    } // Fin de la classe LearnCSharp

    // Il est possible d'inclure plusieurs classes dans un fichier .cs

    public static class Extensions
    {
        // EXTENSION DE FONCTIONS
        public static void Print(this object obj)
        {
            Console.WriteLine(obj.ToString());
        }
    }

    // Syntaxe de déclaration de classe :
    // <public/private/protected/internal> class <class name>{
    //    // champs, constructeurs, fonctions
    //    // tout est déclaré et implémenté à l'intérieur
    // }

    public class Bicycle
    {
        // Propriétés et variable de la classe
        public int Cadence // Public : peut être accédé de partout
        {
            get // get - définit une méthode pour lire la propriété
            {
                return _cadence;
            }
            set // set - définit une méthode pour affecter une valeur à la propriété
            {
                _cadence = value; // 'value' est la valeur passée en argument au setteur
            }
        }
        private int _cadence;

        protected virtual int Gear // Protected : accessible depuis la classe et ses classes filles
        {
            get; // crée une propriété automatique, pas besoin de créer une variable de stockage
            set;
        }

        internal int Wheels // Internal : accessible depuis l'assembly
        {
            get;
            private set; // Il est possible de choisir la portée d'un accesseur
        }

        int _speed; // Par défaut tout est privé au sein d'une classe : accessible uniquement depuis la classe
                    // on peut ajouter explicitement le mot clé 'private'

        public string Name { get; set; }


        // Enum est un type valeur formé par un ensemble de constantes nommées
        // C'est simplement une manière de mettre un nom sur une valeur (int par défaut).
        // Les types compatibles pour un enum sont : byte, sbyte, short, ushort, int, uint, long et ulong.
        // Un enum ne peut pas contenir deux fois la même valeur
        public enum BikeBrand
        {
            AIST,
            BMC,
            Electra = 42, // il est possible de donner explicitement une valeur
            Gitane // 43
        }
        // Nous avons défini cet enum à l'intérieur de la classe Bicycle, c'est donc un type imbriqué
        // Pour le référencer à l'extérieur, il faudra utiliser Bicycle.BikeBrand

        public BikeBrand Brand; // Après avoir déclaré notre type enum, on peut créer un champ de ce type

        // Les membres statiques appartiennent à une classe plutôt qu'à une instance particulière
        // Il est possible d'y accéder sans passer par un objet :
        // ex : Console.WriteLine("Bicycles créés : " + Bicycle.bicyclesCreated);
        static public int BicyclesCreated = 0;

        // Les valeurs en lecture seule sont affectées lors de l'exécution
        // Elles ne peuvent être assignées que lors de leur déclaration ou dans un constructeur
        readonly bool _hasCardsInSpokes = false; // variable en lecture et privée

        // Les constructeurs sont un moyen de créer des objets
        // Voici un constructeur par défaut (pas d'arguments)
        public Bicycle() 
        {
            this.Gear = 1; // accès aux membres de la classe via le mot clé this
            Cadence = 50;  // qui est souvent implicite
            _speed = 5;
            Name = "Bontrager";
            Brand = BikeBrand.AIST;
            BicyclesCreated++;
        }

        // Voici un constructeur spécifique (qui prend des arguments)
        public Bicycle(int startCadence, int startSpeed, int startGear,
                       string name, bool hasCardsInSpokes, BikeBrand brand) 
            : base() // possibilité d'appeler le constructeur de la classe mère (ici Object)
        {
            Gear = startGear; 
            Cadence = startCadence;
            _speed = startSpeed;
            Name = name; 
            _hasCardsInSpokes = hasCardsInSpokes;
            Brand = brand;
        }

        // Les constructeurs peuvent s'enchaîner
        public Bicycle(int startCadence, int startSpeed, BikeBrand brand) :
            this(startCadence, startSpeed, 0, "big wheels", true, brand)
        {
        }

        // Syntaxe de méthode :
        // <public/private/protected> <type de retour> <nom de methode>(<args>)

        // Les classes peuvent implémenter des accesseurs pour leurs champs
        // ou implémenter des propriétés (c'est la méthode dominante en C#)

        // Les paramètres de méthodes peuvent avoir des valeurs par défaut
        // Dans ce cas, la méthode peut être appelée sans arguments
        public void SpeedUp(int increment = 1)
        {
            _speed += increment;
        }

        public void SlowDown(int decrement = 1)
        {
            _speed -= decrement;
        }

        // Les propriétés se chargent de lire/modifier des valeurs
        // elles peuvent être en lecture(get), en écriture(set) ou les deux
        private bool _hasTassles; // variable privée
        public bool HasTassles // propriété publique
        {
            get { return _hasTassles; }
            set { _hasTassles = value; }
        }

        // Il est possible de définir une propriété automatique sur une ligne
        // cette syntaxe créera une variable de stockage automatiquement.
        // Il est possible de modifier l'accèsibilité des getter/setter pour limiter leur utilisation
        public bool IsBroken { get; private set; }

        // La même chose sur plusieurs lignes
        public int FrameSize
        {
            get;
            // Notez que seule la classe Bicycle peut changer la valeur de FrameSize
            private set;
        }

        // Méthode qui affiche la valeur des champs de cet objet
        public virtual string Info()
        {
            return "Gear: " + Gear +
                    " Cadence: " + Cadence +
                    " Speed: " + _speed +
                    " Name: " + Name +
                    " Cards in Spokes: " + (_hasCardsInSpokes ? "yes" : "no") +
                    "\n------------------------------\n"
                    ;
        }

        // Les méthodes peuvent aussi être statiques. Utile pour les méthodes d'aide.
        public static bool DidWeCreateEnoughBycles()
        {
            // À l'intérieur d'une méthode statique on ne peut que référencer des membres statiques !
            return BicyclesCreated > 9000;
        } // Si votre classe n'a que des membres statiques, marquez la comme statique 

    } // fin de la classe Bicycle

    // PennyFarthing est une classe dérivée de Bicycle
    class PennyFarthing : Bicycle
    {
        // Appel au constructeur de la classe mère
        public PennyFarthing(int startCadence, int startSpeed) :
            base(startCadence, startSpeed, 0, "PennyFarthing", true, BikeBrand.Electra)
        {
        }

        protected override int Gear
        {
            get
            {
                return 0;
            }
            set
            {
                // Lève une exception
                throw new ArgumentException("Impossible de modifier Gear sur un PennyFarthing");
            }
        }

        public override string Info()
        {
            string result = "PennyFarthing bicycle ";
            result += base.ToString(); // Appel à la version de base de cette méthode
            return result;
        }
    }

    // Les interfaces contiennent uniquement la signature de leurs membres, sans implémentation.
    interface IJumpable
    {
        void Jump(int meters); // Tous les membres d'interface sont publics par défaut
    }

    interface IBreakable
    {
        bool Broken { get; } // Les interfaces peuvent contenir des propriétés, 
                             // des méthodes et des évènements
    }

    // Une classe ne peut hériter que d'une seule autre classe, mais peut implémenter plusieurs interfaces
    class MountainBike : Bicycle, IJumpable, IBreakable
    {
        int damage = 0;

        public void Jump(int meters)
        {
            damage += meters;
        }

        public bool Broken
        {
            get
            {
                return damage > 100;
            }
        }
    }

    /// <summary>
    /// Utilisé pour illustrer la connexion à une base donnée dans l'exemple LinqToSql
    /// L'approche code first d'EntityFramework est très pratique (un peu comme ActiveRecord de Ruby) 
    /// http://msdn.microsoft.com/fr-fr/data/jj193542.aspx
    /// </summary>
    public class BikeRespository : DbSet
    {
        public BikeRespository()
            : base()
        {
        }

        public DbSet<Bicycle> Bikes { get; set; }
    }
} // Fin du namespace

Sujets non-abordés

Lectures Complémentaires

Convention de codage C#


Vous avez une suggestion ? Peut-être une correction ? Ouvrez un ticket sur Github, ou faites vous-même une pull request !

Version originale par Irfan Charania, mis à jour par 2 contributeur(s).