Développement C# (.NET)

Développement C# (.NET)

Mis à jour le mardi 8 janvier 2013
Notions fondamentales

Les opérateurs

A présent nous avons des types primitifs et des objets créés par nos soins. C'est bien... mais ça manque encore un peu d'interaction. Nous allons à présent apprendre à triturer nos variables en les additionnant, les multipliant, en les disséquant bit par bit :diable: ...

Les opérateurs arithmétiques

Syntaxe de base

Sachez que c'est beau de pouvoir créer des variables, mais cela serait encore plus beau de faire quelque chose avec :p ! Voici donc les cinq opérations de base :

int var1 = 0, var2 = 10, var3 = 5;
var1 = var2 + var3 // L'addition
var1 = var2 - var3 // La soustraction
var1 = var2 * var3 // La multiplication
var1 = var2 / var3 // La division
var1 = var2 % var3 // Le modulo

Nous ne sommes bien sur pas obligés de faire des opérations seulement avec deux opérandes, nous pourrons faire des opérations "plus complexes" :

int a = 32;
int b = 10;
int c = 8;
int resultat = 0;

resultat = a + b - c; //Donnera comme résultat 34
resultat = b * c - a; //Donnera comme résultat 48
resultat = c - b / a; //Donnera comme résultat 8
resultat = a % b; // Cet opérateur est celui du modulo qui sert à donner le reste de la division, donc 2 dans ce cas.

Hé!! Mais j'ai tapé " 8 - 10 / 32 " sur ma calculatrice et ça m'a donné 7,6875. Il n'y pas une erreur dans ce que tu nous dis?

En effet, le résultat que j'ai donné est mathématiquement faux. Je vous propose donc de créer un nouveau projet et de copiez-collé dans "Program.cs" le code suivant:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    static class Program
    {
        static void Main()
        {
            Console.WriteLine("8 - 10 / 32 = " + (8 - 10 / 32).ToString());
        }
    }
}

Si vous exécutez le programme (touche F5 de votre clavier ou dans le menu "Debug/Démarrer le débogage" ) vous obtiendrez :

8 - 10 / 32 = 8

Vous voyez que le résultat donné par le programme est 8. Ceci est dû au fait que les opérations sont faites à partir d'entiers, donc le résultat est tronqué, dans ce cas là "10 / 32" donnera 0 et 8 - 0 = 8 !!!

Donc retenez bien qu'il faut faire très attention aux types de variables que vous choisissez. Pour obtenir un résultat mathématiquement correct, il aurait fallu travailler avec des types décimaux. Vous pouvez essayer le programme suivant pour un résultat plus proche de la vérité mathématique:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    static class Program
    {
        static void Main()
        {
            float a = 8;
            float b = 10;
            float c = 32;
            Console.WriteLine("8 - 10 / 32 = " + (a - b / c).ToString());
        }
    }
}

Syntaxe simplifiée

Réaffectation simple

Certaines syntaxes peuvent être raccourcies, notamment lorsque la variable affectée est reprise lors de l'affectation. Par exemple dans le code suivant:

int entier1 = 3;
entier1 = entier1 + 2;

Nous voyons que entier1 est réutilisé pour réaffecter sa propre valeur, une syntaxe simplifiée donnant le même résultat sera celle-ci:

int entier1 = 3;
entier1 += 2;// += signifie que nous ajoutons la valeur de la variable de droite à la valeur de la variable de gauche

Cette syntaxe est un peu difficile à appréhender au début car par rapport à la syntaxe de base que nous connaissons depuis le primaire, celle-ci est complètement nouvelle. Sa décomposition est la suivante:

{variableAReaffecter} {operation}= {operateur}

Voici d'autres exemple impliquant les cinq opérateurs de base:

int entier1 = 5;
entier1 += 4;//on ajoute 4 à entier1
entier1 -= 3;//on soustrait 3 à entier1
entier1 *= 2;//on multiplie entier1 par 2
entier1 /= 3;//on divise entier1 par 3
entier1 %= 2;//entier1 vaudra le reste de sa propre division par 2
Post/pré incrémentation/décrémentation

Parmi nos utilisations de la syntaxe simplifiée précédente, un grand nombre est avec l'opérateur plus ( + ) ou moins ( - ) et l'opérande 1. Si bien qu'il existe une syntaxe encore plus courte pour dire "j'ajoute 1" ou "je soustrais 1".
Seulement, trois cas d'utilisations peuvent s'ouvrir à nous :
-Nous voulons simplement ajouter ou soustraire 1.
-Nous voulons ajouter ou soustraire 1, puis récupérer la nouvelle valeur.
-Nous voulons récupérer la valeur puis ajouter ou soustraire 1.

Dans chaque cas nous utiliserons l'opérateur d'incrémentation "++" ou de décrémentation "--" (le premier pour ajouter 1, le second pour soustraire 1). Seulement, en fonction des cas, nous le placerons avant ou après notre variable. Placé avant, la valeur récupérer sera celle d'après l'opération; placé après, la valeur récupérée sera celle d'avant l'opération.

Voyons un petit exemple:

int entier1 = 5, entier2 = 5;
Console.WriteLine("Entier1 vaut " + ++entier1 + " et Entier2 vaut " + entier2++);
Console.WriteLine("Entier1 vaut " + entier1 + " et Entier2 vaut " + entier2);

Nous déclarons et initialisons deux variables ayant la même valeur ( 5 ), puis nous affichons leur valeur tout en utilisant l'opérateur d'incrémentation. Le résultat affiché sera celui-ci:

Entier1 vaut 6 et Entier2 vaut 5
Entier1 vaut 6 et Entier2 vaut 6

Nous voyons qu'au final, les deux variables ont été incrémentées (à la seconde ligne du résultat, elles sont la même valeur), seulement l'opérateur d'incrémentation n'a pas agis au même moment lors du premier Console.WriteLine, pour entier1 nous avons effectué de la pré-incrémentation, c'est à dire que nous avons ajouter 1 à sa valeur, puis nous avons retourné ladite valeur ( 6 ); quant à Entier2, nous avons effectué une post-incrémentation, c'est à dire que nous avons retournée sa valeur ( 5 ) puis nous lui avons ajouté 1.

L'incrémentation et la décrémentation sont très utilisées, dans la plupart de mes fichiers de code, on retrouve au moins une fois un des deux opérateurs. Cette logique n'est d'ailleurs pas propre au C# mais utilisées dans de nombreux autre langages de programmation!

Les opérateurs logiques

Cette partie sera très importante avant d'aborder les conditions et les boucles, nous allons voir les expressions booléennes, qui en sont le pilier principal.

Expression booléenne c'est quoi ce gros mot? o_O

Une expression booléenne c'est tout simplement une expression (un petit morceau de code) qui renvoi une de ces deux valeurs : vrai ou faux.
En C#, qui ne parle pas le français, c'est la traduction anglaise qui est utilisée : true ou false.

Le terme booléen viens du nom d'un mathématicien : George Boole.

Pourquoi a-t-on besoin des booléens?

La réponses est simple : une langage de programmation est un intermédiaire entre un langage compréhensible par l'humain et un langage machine. Or nous (les humains) fonctionnons avec une logique dite "floue", sans entrer dans de la psychanalyse ou de la métaphysique, nous pouvons tout simplement prendre des décisions par nous même en analysant une situation. L'ordinateur, quant à lui, en est totalement incapable, en effet il fonctionne avec logique dite "binaire", il ne comprend que le vrai et le faux. Par exemple nous sommes capables de dire qu'un rouge vif et un rouge sombre sont tout les deux rouges, de son côté, l'ordinateur ne verra aucune ressemblance et prendra les couleurs comme totalement différentes. Ce sera donc à nous de lui expliquer que si c'est rouge vif ou rouge sombre alors c'est rouge.

Voilà pourquoi nous devons nous débrouiller pour lui exprimer quelque chose qui puisse être seulement vrai ou faux.

Et on réalise cela au moyen de l'algèbre de Boole (oui je sais c'est des maths :p ), qui met à notre disposition, en plus de vrai ou faux des opérateurs booléens.
Pas de mystères là dessus : de la manière qu'on a des opérateurs pour addition et multiplier les nombres classiques, on a des opérateurs pour combiner les booléen : ET et OU.

Voici comment on déclare et utilise une variable booléenne en C# :

bool b;    // déclare un booléen
b = true;  // la valeur de b devient vrai
b = false; // la valeur de b devient faux
L'opérateur ET

De la même manière qu'il y a un certain nombre d'opérateurs défini pour effectuer des opérateurs sur les nombres, on a plusieurs opérateurs disponibles pour travailler avec nos valeurs booléennes.
Voyons le premier d'entre eux : l'opération ET, qu'on retrouve aussi très souvent appelée AND, son nom anglais.

Attends un peu, ton ET là, ce serais pas la même chose que le ET en français?

Bien vu ;)
Pour que le résultat d'une opération ET sur deux booléens soit vraie, il faut que les deux soit vrai. Dans les autres cas la valeur renvoyée est faux.

Exemple en français : J'irai acheter du pain si j'ai le tempsetde l'argent.
En italique j'ai mis les deux valeurs booléennes qui doivent être à vraie pour que je puisse acheter du pain. Si une est fausse ce ne sera pas possible.

De manière plus formelle on a :

Variable 1

Variable 2

Résultat

vrai

vrai

vrai

vrai

faux

faux

faux

vrai

faux

faux

faux

faux

Dans le code, on réalise l'opération grâce à l'opérateur &&.
Exemple a tester :

bool a = true && true;
bool b = true && false;
bool c = false && true;
bool d = false && false;

Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
L'opérateur OU

Il arrive également que nous voulions qu'au moins une des conditions soit correcte, par exemple : je serai content s'il achète de la confiture de fraiseoude framboise.

Ici il faut faire un peu plus attention car le sens n'est pas tout à fait le même qu'en français courant car ici la condition est en fait réalisée si
- de la confiture de fraise est achetée
- de la confiture de framboise est achetée
- les deux !
C'est ce dernier cas qui différencie le OU (OR en anglais) français de l'opérateur OU utilisé en informatique.

Voici de nouveau un petit tableau récapitulatif :

Variable 1

Variable 2

Résultat

vrai

vrai

vrai

vrai

faux

vrai

faux

vrai

vrai

faux

faux

faux

Ce tableau montre la logique de l'opérateur, évidemment dans le code c'est true et false qui sont utilisés.

Dans le code, on utilise l'opérateur || (deux barres verticales).
Vous pouvez le tester en modifiant simplement le code que je vous ai donné précédemment.

L'opérateur OU exclusif

Enfin, il arrive que nous voulions que une et une seule des conditions soit réaliser, par exemple lorsque notre mère nous disait : "Tu as le droit à du gâteau ou à une glace, mais pas les deux".
Ceci se rapproche donc plus du sens du mot "ou" de la langue française car nous l'utilisons souvent pour exprimer une exclusivité.

Puisque cela vous manquait, voici encore un tableau récapitulatif :) :

Variable 1

Variable 2

Résultat

vrai

vrai

faux

vrai

faux

vrai

faux

vrai

vrai

faux

faux

faux

Voyons tout de même l'exemple du gâteau et de la glace en C#:

bool gateau = true;
bool glace = false;
Console.WriteLine(gateau ^ glace);//true

glace = true;
Console.WriteLine(gateau ^ glace);//false

Nous pouvons donc bien avoir du gâteau ou de la glace, mais pas les deux !!

L'égalité

Une des opérations booléennes dont nous aurons le plus besoin dans peu de temps est l'égalité.
Tout comme en mathématique il s'agit ici de tester si la valeur de deux variables sont égale.

Le second piège, c'est que comme le symbole = (égal) traditionnellement utilisé est déjà pris pour l'affectation, c'est le symbole == (deux signes égal) qui sert à faire des comparaisons d'égalité.

Le résultat d'une égalité renvoie true si les deux valeurs sont égales et false sinon.

int a = 3;
Console.WriteLine(a == 3);
Console.WriteLine(a == 4);

Ici nous créons un entier, et nous lui assignons la valeur 3. Ensuite nous vérifions s'il est égal à 3 (vrai), puis si il est égal à 4 (faux).

Il est tout a fait possible d'utiliser l'assignation et la comparaison dans une même expression comme dans l'extrait suivant :

bool unBooleen;
int unEntier = 3;
unBooleen = unEntier + 1 == 4;

Le sens de cette instruction peut ne pas paraitre évident au premier abord. Pour l'analyser, il faut bien avoir en tête la priorité des opérations:

  • Parenthèses

  • Multiplication, divisions

  • Addition, soustraction

  • Comparaison

  • Assignation

Il est bien entendu toujours possible d'utiliser des parenthèses afin de modifier l'ordre dans le lequel les opérations d'une expression sont résolues.

Ce qui donnerait successivement:

bool unBooleen;
int unEntier = 3;
unBooleen = unEntier + 1 == 4;
unBooleen = 3 + 1 == 4;
unBooleen = 4 == 4;
unBooleen = true;

Bon, j'avoue que l'exemple est un peu tiré par les cheveux, mais faire ainsi peut se révéler très utile et peut parfois même nous éviter d'utiliser des conditions!!

Les autres comparaisons.

En C# comme ailleurs on n'a pas forcément envie de seulement faire des comparaisons d'égalité, on voudrait aussi savoir qui est le meilleur...

Ne cherchez pas c'est moi. :D

On a donc à notre disposition d'autres opérateurs de comparaisons, les même qu'on retrouve en maths, à savoir :

Symbole

Opération

>

supérieur à

<

inférieur à

>=

supérieur ou égal à

<=

inférieur ou égal à

!=

différent de

Pour les quatre premiers symboles, rien de bien compliqué, l'opération parle d'elle même :

int a = 3;
int b = 4;
int c = 4;
Console.WriteLine(a>b);//false
Console.WriteLine(b<c);//false
Console.WriteLine(b>=c);//true
Console.WriteLine(a<=b);//true

Pour l'opérateur "différent" ( != ), il retournera l'inverse de ce que l'on obtient avec l'opérateur "égal" ( == ):

int a = 3;
int b = 4;
int c = 4;
Console.WriteLine(a!=b);//true
Console.WriteLine(b!=c);//false

Nous pouvons de même utiliser cet opérateur en alternative au "^" pour signifier un "OU exclusif" qui retournera la valeur vrai si et seulement si l'une des deux opérandes est vrai. Mais préférez quand même l'opérateur "^" pour les "OU exclusif", il est là pour ça ;) .

Les négations

Promis, c'est le dernier des opérateur des expressions booléennes :D
Nous le voyons en dernier car il faut le distinguer des tous les autres :
En effet, tous les opérateurs vus auparavant sont des opérateur binaire, il s'utilisent pour deux opérandes. L'opérateur de négation quant à lui est un opérateur unaire, il ne s'applique que pour une seule opérande.

Cet opérateur est le point d'exclamation ( ! ), il se place devant un booléen ou devant une expression booléenne dont on veut le contraire, par exemple:

bool gateau = true;
bool glace = false;
Console.WriteLine(gateau);//true
Console.WriteLine(!gateau);//false
Console.WriteLine(glace);//false
Console.WriteLine(!glace);//true

Console.WriteLine(gateau != glace);//true
Console.WriteLine(!(gateau != glace));//false

Notez que pour le dernier affichage, j'ai ajoutés des parenthèses supplémentaire, en effet, sans elles, l'opérateur ! ne se serait pas appliqué à l'expression "gateau != glace" mais seulement à "glace", ce qui n'aurait pas donné le même résultat.

Les opérateurs bit à bit

Cette partie vient volontairement en dernier, en effet, les opérateurs bit à bit ne sont plus beaucoup utilisés en C# étant donné qu'il existe le type booléen qui permet une utilisation plus aisée. Mais il peut nous arriver de vouloir effectuer des opérations directement sur les bits de nos variables. Comme nous allons parler des différentes bases de calculs et pour ne pas qu'il y est des ambiguïtés, j'écrirais au maximum les nombres en toute lettre (vingt-trois au lieu de 23 par exemple)

Mais avant toute chose, voyons quelques rappels sur les nombres et les bases de calculs.

Les variables : dissection

Dans notre monde occidental (et même dans la plupart des peuples) nous comptons en base "dix", c'est à dire qu'avec un seul chiffre nous pouvons avoir "dix" nombres (0,1,2,3,4,5,6,7,8 et 9). La raison est toute simple : nous avons 10 doigts, du coup dès les premières utilisations des nombres, la base "dix" s'est très vite imposée au sein des gens n'ayant pas forcément reçu d'éducation et n'ayant donc pas vraiment appris à calculer, c'est si facile de compter sur les doigts ;) .

Les bases de calcul

Mais des gens plus instruits (les mathématiciens :p ) ont mis en place plusieurs règles et plusieurs nombres pour compter plus loin que ce que permet un seul chiffre, et cela dans n'importe quelle base (des aztèques comptaient en base "vingt"), ces règles sont entre autre:

  • Un nombre se lit de droite à gauche, le chiffre le plus à droite a la position 0, celui juste à sa gauche la position 1, etc..

  • Un nombre s'écrivant CnCn-1Cn-2...C2C1C0 en base b peut se décomposer en Cn*b^n + Cn-1*b^(n-1) + .. + C1*b^(1) + C0*b^(0)

Ici, ce sont les règles des bases qui nous intéressent le plus, pour d'autres règles voir wikipedia ^^

Ces règles appliquées à la base "dix" pour le nombre 1024 (en base dix) par exemple, font que le 4 a l'index 0, le 2 à l'index 1, etc... et que ce nombre peut se décomposer en 1*10^3+0*10^2+2*10+4*1. C'est donc bon pour la base "dix".

Du côté des ordinateurs

Sauf que du côté de nos ordinateurs, ça ne se passe pas pareil... notre ordinateur n'a pas dix doigts!! Globalement, il a juste une suite de circuit imprimés dont chaque composant primaire n'a que deux états : 0 (le courant ne passe pas, le composant n'est pas alimenté) et 1 (le courant passe, le composant est alimenté). Du coup, avec un seul "composant", il pourra juste compter deux nombres : zéro et un. Bien sûr avec plusieurs bits (0 et 1 sont des bits), il peut aller plus loin que un. Ce qui a donné la base "deux" ou dites "binaire". Cette base fonctionne comme la base "dix", sauf qu'à deux on passe à l'index suivant au lieu d'écrire "2", ce qui donne par exemple pour 10110 (vingt-deux): 1*2^4+0*2^3+1*2^2+1*2+0*1. Une fois que vous savez décomposer les nombres binaires, vous pouvez épater vos amis en soirées en comptant jusqu'à 31 avec une seule main :p .

Nos variables

Comme nous l'avons dit dans le chapitre sur les types élémentaires, chaque type est codé sur un certain nombre d'octets (suite de 8 bits), ce qui donne pour un byte (byte = octet en anglais) un octet, donc huit bits ce qui nous fait 2^8 valeurs potentielles, un byte étant non signé ses valeurs peuvent aller de 0 à 255. Le principe est de même pour les types d'entier non-signés (uint, ushort, ..) et char , seul le nombre d'octets disponibles change.

Et les nombres négatifs ?
Etant donné que nous ne pouvons pas avoir de bits négatifs, il nous faut trouver un moyen de coder des nombres négatifs. La première solution retenue a été de prendre le bit de poids fort (celui qui vaut le plus, avec l'index le plus élevé, celui le plus à gauche) et de le mettre à 0 pour représenter un nombre positif et à 1 pour un nombre négatif. Mais cette solution a vite été abandonnée pour deux raisons:

    Désavantages de jouer seulement sur le bit de poids fort

  • Dans ce cas, il y a deux manières de coder le nombre 0, par exemple pour un nombre sur un octet : 0 s'écrit 0000 0000 et -0 s'écrit 1000 0000. Or, en mathématique 0 et -0 doivent être égaux, mais là ce n'est pas le cas, 1000 0000 est différent de 0000 0000 !!!

  • Ensuite, pour effectuer des additions, cela ne fonctionnait pas : imaginons l'opération -4 + 3, en binaire sur un octet cela donnerait 1000 0100 + 0000 0011 = 1000 0111, or dans ce cas 1000 0111 égale à -7 en décimal (au lieu de -1)

On a donc décidé de fonctionner avec le complément à 2, son fonctionnement est simple : pour calculer un nombre négatif, on prend son opposé (donc le positif), on inverse tout ses bits et on ajoute 1.
Par exemple pour "moins cinq" codé sur quatre bits:
On écrit le nombre positif : 0101
On inverse tout ses bits : 1010
Et on ajoute 1 : 1011

Mais quel est l'intérêt me direz-vous ?
Et bien l'intérêt est qu'il n'y a pas les deux inconvénients de tout à l'heure :D

    Avantages du complément à 2

  • Il n'y a qu'une seule manière de coder le nombre 0, si nous tentons de trouver -0, cela nous donnera 1111 (0000 inversé) + 1 = 0000 (on oublie le dépassement). C'est déjà mathématiquement mieux quand -0 = 0 non ? :)

  • Ensuite, lorsque l'on additionne, le résultat est juste. Si nous reprenons le calcul de tout à l'heure (moins quatre + trois) cela nous donne 1111 1100 + 0000 0011 = 1111 1111 et dans ce cas 1111 1111 égale bien à -1 (en décimal)!! Vous êtes dubitatif ? Pas de problèmes : je vais décomposer ce nombre pour trouver sa valeur. On voit que 1111 1111 est négatif (bit de poids fort égal à 1), donc pour trouver son opposé (sa valeur positive) on retranche 1 : 1111 1110 et on inverse tout les bits : 0000 0001 ce qui vaut bien 1 en décimal.

Je ne m'attarderais pas plus sur les calculs binaires, néanmoins si vous voulez en savoir plus, allez regarder ce cours.

Les différents opérateurs binaires

Là nous n'avons que six opérations différentes:

Opération

Opérateur

ET

&

OU

|

OU Exclusif

^

Décalage vers la gauche

<<

Décalage vers la droite

>>

Inversion

~

Le ET

Le ET va effectuer un ET logique sur chaque bit de deux nombres que l'on compare et va nous renvoyer le résultat, par exemple sur un octet, onze et quatorze vont donner dix, logique non ? :D Une petite explication s'impose je vois ;) :
0000 1011 : onze
0000 1110 : quatorze
On ne va réécrire que les 1 communs (facile si on écrit les deux nombre l'un au dessus de l'autre)
0000 1010 : dix !

Un intérêt que va avoir cet opérateur va être le calcul de masque de sous réseau !!

Imaginons deux adresses IP locales de classe C : 192.168.8.7 / 29 et 192.168.8.11 / 29
Le masque de sous-réseau est donc 255.255.255.248 et nous aimerions savoir si ceux deux adresses sont sur le même sous réseau, nous allons faire un ET sur la dernière partie de chaque adresse IP avec la dernière partie du masque et les comparer:

static void Main(string[] args)
{
    Console.Write("Entrez le dernier octet de l'adresse 1 : ");
    byte adresse1 = byte.Parse(Console.ReadLine());
    Console.Write("Entrez le dernier octet de l'adresse 2 : ");
    byte adresse2 = byte.Parse(Console.ReadLine());
    Console.Write("Entrez le dernier octet du masque : ");
    byte masque = byte.Parse(Console.ReadLine());
    Console.WriteLine("Les deux adresses sont sur le même réseau : " + ((adresse1 & masque) == (adresse2 & masque)));
}

Avec ce petit programme, pas besoin de sortir un morceau de papier et un crayon ou notepad pour calculer si nos deux adresses sont sur le même réseau, il nous suffit de tester l'identité des 3 premiers octets (ceux avec les parties de masque en 255), de lancer notre programme pour tester le dernier octer et c'est bon, vous pouvez déjà créer un programme en C# pour votre administrateur réseau :D

Le OU

Le OU va effectuer un OU logique sur chaque bit de deux nombres que l'on compare et va nous renvoyer le résultat, toujours le même exemple : sur un octet les nombres onze et quatorze vont donner quinze:
0000 1011 : onze
0000 1110 : quatorze
On réécrit un 1 si dans un ou dans deux des nombres il y a un 1 à cette position
0000 1111 : quinze !

Mais quel est l'intérêt cette fois-ci ?

L'intérêt ici se situe vers les énumérations. Vous vous souvenez des conversions entier vers enum et enum vers entiers que nous faisions ? Et bien en réalité, nos énums, on peut décider nous même explicitement quel valeur entière va prendre chacun des membres de notre énumération. Par exemple pour une enum "Dessert":

[Flags]// [Flags] est un attribut servant à indiquer que les valeurs de notre enumeration peuvent être combinees
enum Dessert
{
    Yaourt = 1,//En binaire yaourt vaut 0001
    Gateau = 2,//En binaire vaut 0010
    Fruit = 4,//En binaire vaut 0100
    Glace = 8 // En binaire vaut 1000
}

Vous remarquez que chaque membre est représenté par un seul bit à 1, de poids plus ou moins fort. Ainsi, grâce au OU bit à bit, on pourra avoir une valeur énumérative qui vaudra plusieurs de ces membres en même temps.

static void Main(string[] args)
{
    Dessert monDessert = Dessert.Yaourt | Dessert.Gateau;//Ici monDessert vaudra en même temps Yaourt et Gateau
    Console.WriteLine(monDessert);//Affichera "3" (2 | 1)
}

Par la suite, quand on vérifiera la valeur de "monDessert", au lieu de tester directement l'égalité, on effectuera un ET binaire avant de comparer le résultat à une valeur de l'énum :

static void Main(string[] args)
{
    Dessert monDessert = Dessert.Yaourt | Dessert.Gateau;

    bool estYaourt = (monDessert & Dessert.Yaourt) == Dessert.Yaourt;
    bool estFruit = (monDessert & Dessert.Fruit) == Dessert.Fruit;
    bool estGateau = (monDessert & Dessert.Gateau) == Dessert.Gateau;
    bool estGlace = (monDessert & Dessert.Glace) == Dessert.Glace;

    Console.WriteLine("Yaourt : " + estYaourt);
    Console.WriteLine("Fruit : " + estFruit);
    Console.WriteLine("Gateau : " + estGateau);
    Console.WriteLine("Glace : " + estGlace);
}

Ce qui donnera

Yaourt : True
Fruit : False
Gateau : True
Glace : False
Press any key to continue . . .
Le OU exclusif

Dans la même série que le OU bit à bit, vient le OU exclusif bit à bit. Il va effectuer un OU exclusif sur chaque bit et renvoyer le résultat. Toujours le même exemple : sur un octet les nombres onze et quatorze vont donner cinq:
0000 1011 : onze
0000 1110 : quatorze
On réécrit un 1 si dans un et un seul de ces nombres il y a un 1 à cette position.
0000 0101 : cinq!

L'intérêt va être voisin avec l'exemple de tout à l'heure, cette fois-ci nous pourront retrancher un des membres de l'énum si il y est.
Par exemple dans le code suivant, on a ajouté la glace en trop et on aimerait l'enlever

static void Main(string[] args)
{
    Dessert monDessert = Dessert.Yaourt | Dessert.Gateau | Dessert.Glace;
}

On va juste fait un OU Exclusif entre monDessert et Dessert.Glace pour obtenir monDessert sans glace. Puis on va vérifier (au cas où :p ) si la glace a bien disparu :

static void Main(string[] args)
{
    Dessert monDessert = Dessert.Yaourt | Dessert.Gateau | Dessert.Glace;//On a mis le yaourt, le gateau, et la glace

    bool estYaourt = (monDessert & Dessert.Yaourt) == Dessert.Yaourt;
    bool estFruit = (monDessert & Dessert.Fruit) == Dessert.Fruit;
    bool estGateau = (monDessert & Dessert.Gateau) == Dessert.Gateau;
    bool estGlace = (monDessert & Dessert.Glace) == Dessert.Glace;

    Console.WriteLine("Yaourt : " + estYaourt);
    Console.WriteLine("Fruit : " + estFruit);
    Console.WriteLine("Gateau : " + estGateau);
    Console.WriteLine("Glace : " + estGlace);

    monDessert ^= Dessert.Glace; // Equivalent à monDessert = monDessert ^ Dessert.Glace
    Console.WriteLine("On a enlevé la glace");
    Console.WriteLine();

    estYaourt = (monDessert & Dessert.Yaourt) == Dessert.Yaourt;
    estFruit = (monDessert & Dessert.Fruit) == Dessert.Fruit;
    estGateau = (monDessert & Dessert.Gateau) == Dessert.Gateau;
    estGlace = (monDessert & Dessert.Glace) == Dessert.Glace;

    Console.WriteLine("Yaourt : " + estYaourt);
    Console.WriteLine("Fruit : " + estFruit);
    Console.WriteLine("Gateau : " + estGateau);
    Console.WriteLine("Glace : " + estGlace);
}

Nous obtiendrons la sortie suivante :

Yaourt : True
Fruit : False
Gateau : True
Glace : True
On a enlevé la glace

Yaourt : True
Fruit : False
Gateau : True
Glace : False
Press any key to continue . . .

On remarque bien que la glace était présente au début et qu'après elle n'y est plus.

On aurait pas pu faire ça simplement avec soustraction ? onze - huit fait bien trois !

La réponse est oui et non :lol: En effet, dans notre cas ça fonctionne car nous avons bien la glace dans monDessert, la différence de comportement se serait déroulée si nous n'avions pas la glace au départ dans monDessert. Dans ce cas là, le OU exclusif n'enlèvera la glace que si elle est présente !! Alors qu'avec la soustraction, que la glace y soit ou pas, on tentera de la retirer.

Les décalages

Cette partie est un peu particulière, à titre personnel (je n'ai pas non plus 20 ans d'expérience en C# :p ), je n'ai encore jamais utilisé les décalages de bits. Cela serait néanmoins dommage que je ne vous les explique pas :D . Ces opérations vont être équivalentes aux multiplications et divisions par 2^n (où n un entier correspondant à l'amplitude du décalage) par exemple par 2, 4, 8, 16 etc..
Les décalages sont simples, on prend les bits d'un nombre, et on les décale tous d'un ou plusieurs bits (vers la gauche ou la droite selon les cas), les bits "dépassant du cadre" sont oubliés et les bits ajoutés sont des 0.

Prenons le nombre "onze" : 0000 1011, en décalant d'un bit vers la droite, cela donnera 0000 0101, soit "cinq". En décalant d'un bit vers la gauche, cela donnera 0001 0110, soit "vingt-deux". C'est aussi simples que cela, on décale juste les bits en complétant par des zéros.

Il reste néanmoins certaines particularités comme pour les nombres négatifs, en effet, en décalant vers la droite, on devrait forcément obtenir un nombre positif à partir d'un nombre négatif (le bit de signe 1 est remplacé par un 0), or ce n'est pas le cas, les bits de signes sont gardés, les décalages ne se font que sur les bits de valeurs. Mais voyons ce qu'on décalage de un bit sur la droite peut donner sur les nombres de - 3 à 3 :

static void Main(string[] args)
{
    Console.WriteLine("-3 : " + (-3>>1).ToString());
    Console.WriteLine(" 3 : " + (3 >> 1).ToString());
    Console.WriteLine("-2 : " + (-2 >> 1).ToString());
    Console.WriteLine(" 2 : " + (2 >> 1).ToString());
    Console.WriteLine("-1 : " + (-1 >> 1).ToString());
    Console.WriteLine(" 1 : " + (1 >> 1).ToString());
    Console.WriteLine(" 0 : " + (0 >> 1).ToString());
}

Et le résultat sera :

-3 : -2
 3 : 1
-2 : -1
 2 : 1
-1 : -1
 1 : 0
 0 : 0
Press any key to continue . . .

On voit que cela correspond ici à une division par 2.

Quant aux décalages vers à la gauche :

static void Main(string[] args)
{
    Console.WriteLine("-3 : " + (-3 << 1).ToString());
    Console.WriteLine(" 3 : " + (3 << 1).ToString());
    Console.WriteLine("-2 : " + (-2 << 1).ToString());
    Console.WriteLine(" 2 : " + (2 << 1).ToString());
    Console.WriteLine("-1 : " + (-1 << 1).ToString());
    Console.WriteLine(" 1 : " + (1 << 1).ToString());
    Console.WriteLine(" 0 : " + (0 << 1).ToString());
}

Et cela affichera:

-3 : -6
 3 : 6
-2 : -4
 2 : 4
-1 : -2
 1 : 2
 0 : 0
Press any key to continue . . .

Nous voyons que cela correspond à une multiplication par 2

L'inversion

Pour ne pas trop vous décourager, j'ai choisi volontairement de finir par l'opérateur bit à bit le plus simple : l'opérateur d'inversion des bits. En effet, le plus complexe avec cet opérateur, c'est de l'écrire :lol: (sur un clavier azerty : Alt Gr + 2 ). Ensuite, il suffit de comprendre comment s'écrit un nombre en binaire et différencier le 0 du 1 :D .

Comme son nom l'indique, cet opérateur va prendre tout les bits d'un nombre et les inverser, ainsi tout les 0 vont devenir 1 et tout les 1 vont devenir 0. Voyons par exemple le nombre 4 :

Console.WriteLine(~4);

Qui affichera :

-5
Press any key to continue . . .

Faisons tout de même une petite vérification pour la forme, nous écrivons 4 en binaire (pour simplifier, sur quatre bits): 0100, nous inversons ses bits : 1011, nous remarquons que c'est un nombre négatif (bit de poids fort à 1) donc nous recherchons sont opposés en retranchant 1 : 1010 puis nous inversons les bits : 0101 ce qui donne bien 5 !

Vous voyez que juste avec des types de bases, on peut en faire des choses :waw: et ce n'est pas fini !! Tout cela, nous pourrons aussi le faire sur nos propres objets! Mais tout d'abord, nous allons ajouter de la logique supplémentaire à nos programmes grâce aux conditions et aux boucles !!

Exemple de certificat de réussite
Exemple de certificat de réussite