Un opérateur prend un, deux ou trois opérandes et produit une valeur : a + b, !done, x ?? fallback. Les opérateurs de C# sont proches de ceux de C, Java et JavaScript, mais certains se comportent d'une façon qui surprend les débutants, surtout la division entière et le signe de %.
Opérateurs arithmétiques
+, -, *, / et % fonctionnent sur tous les types numériques. Le type du résultat dépend des opérandes : deux valeurs int produisent un int, un int et un double produisent un double.
Sortie :
22
12
85
3
2
3.4
3.4
3
3.4
La division entière tronque vers zéro. 17 / 5 vaut 3 et -17 / 5 vaut -3. Cela provoque un bug très courant : int percent = done / total * 100; vaut 0 dès que done < total, car done / total vaut déjà 0. Multipliez d'abord (done * 100 / total) ou divisez en double.
Diviser un entier ou un decimal par zéro lève DivideByZeroException. Diviser un double par zéro ne lève rien ; cela produit l'infini, ou NaN pour 0.0 / 0.0 :
Sortie :
True
True
False
DivideByZeroException
L'opérateur modulo et les nombres négatifs
% renvoie le reste de la division, et son résultat prend le signe de l'opérande de gauche. C'est différent de Python et du « mod » mathématique, qui sont toujours positifs ou nuls pour un diviseur positif :
Sortie :
1
-1
1
2
even
62 min 5 s
Thu
1.5
Utilisez la fonction Mod dès que la partie gauche peut être négative et que le résultat sert d'index : days[(today + offset) % 7] lèverait une IndexOutOfRangeException pour une somme négative. Tester si un nombre est impair présente le même piège : n % 2 == 1 vaut false pour les nombres impairs négatifs ; testez n % 2 != 0.
Affectation, affectation composée et incrémentation
= affecte. Les formes composées +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= combinent une opération et une affectation. ++ et -- ajoutent ou retirent 1, et existent en forme préfixe et suffixe, qui diffèrent par la valeur renvoyée :
Sortie :
14
start, step
a=5 b=7 i=7
4
Le cas du byte montre une particularité : level = level + 10; ne compile pas, car byte + int est un int, mais level += 10; compile, car l'affectation composée inclut un cast implicite vers le type de la variable. Ce cast boucle en cas de dépassement (250 + 10 devient 4).
Opérateurs de comparaison et logiques
Les opérateurs de comparaison (==, !=, <, >, <=, >=) renvoient un bool. Pour les chaînes, == compare le texte, pas les références. Les opérateurs logiques sont ! (non), && (et), || (ou) et ^ (ou exclusif).
Sortie :
True
no active user
with ||:
evaluated A
with |:
evaluated A
evaluated B
True
&& et || court-circuitent : l'opérande de droite n'est évalué que lorsqu'il peut changer le résultat. C'est ce qui rend user != null && user.IsActive sûr. Les opérateurs simples & et | sur des booléens évaluent toujours les deux côtés, ce qui est rarement ce que l'on veut dans une condition.
Opérateurs bit à bit et de décalage
Sur les types entiers, &, |, ^ et ~ agissent sur les bits individuels, et << et >> les décalent. On s'en sert pour les drapeaux, les masques, le hachage et les protocoles bas niveau :
Sortie :
3
True
False
101
1024
125
5
-1
Pour des ensembles nommés de drapeaux, un enum avec l'attribut [Flags] est plus clair que des constantes entières, et HasFlag se lit mieux qu'un masque. C# 11 a ajouté >>>, un décalage à droite non signé qui remplit avec des zéros même pour les nombres négatifs.
Opérateurs null
Trois opérateurs servent à manipuler des valeurs qui peuvent être null :
Sortie :
Guest
0
5
a ?? b renvoie a sauf s'il vaut null, et sinon b. a?.Member renvoie null au lieu de lever une exception quand a vaut null. C# 8 a ajouté a ??= b, qui affecte b à a seulement quand a vaut null. Ils sont traités en détail dans types nullables.
Priorité des opérateurs
Quand une expression mélange des opérateurs, les lignes du haut lient plus fortement. Les opérateurs d'une même ligne se groupent de gauche à droite, sauf l'affectation, ?? et ?:, qui se groupent de droite à gauche. (Les opérandes eux-mêmes sont toujours évalués de gauche à droite.)
| Catégorie | Opérateurs |
|---|---|
| Primaires | x.y, x?.y, f(x), a[i], x++, x--, new, typeof, nameof |
| Unaires | +x, -x, !x, ~x, ++x, --x, (T)x, await |
| Multiplicatifs | *, /, % |
| Additifs | +, - |
| Décalage | <<, >>, >>> |
| Relationnels et test de type | <, >, <=, >=, is, as |
| Égalité | ==, != |
| ET bit à bit | & |
| OU exclusif bit à bit | ^ |
| OU bit à bit | | |
| ET conditionnel | && |
| OU conditionnel | || |
| Fusion null | ?? |
| Conditionnel | c ? a : b |
| Affectation et lambda | =, +=, -=, ??=, => et les autres formes composées |
Deux conséquences à connaître : a + b * c multiplie d'abord, et x & mask == 0 compare d'abord (== lie plus fortement que &), donc cela signifie x & (mask == 0) et ne compile pas pour des entiers. Écrivez (x & mask) == 0. Les parenthèses ne coûtent rien et rendent l'intention évidente.
Surcharge d'opérateurs
Vos propres types peuvent définir la signification des opérateurs. C'est ainsi que decimal, DateTime et TimeSpan prennent en charge + et < :
Sortie :
33.49
True
Les opérateurs doivent être public static, et certains vont obligatoirement par paires : définir < impose >, et == impose !=. Avec == défini, le compilateur émet aussi un avertissement tant que vous n'avez pas redéfini Equals et GetHashCode en cohérence. Ne surchargez les opérateurs que là où le sens est évident, comme pour les nombres, les vecteurs et l'argent.
Questions fréquentes
Que fait % en C# ?
% est l'opérateur de reste : 17 % 5 vaut 2. Le résultat a le signe de l'opérande de gauche, donc -7 % 3 vaut -1, et non 2 comme en Python. Pour un résultat toujours positif ou nul, comme il en faut pour faire boucler un index, écrivez ((a % n) + n) % n. Il fonctionne aussi sur double et decimal : 7.5 % 2 vaut 1,5.
Pourquoi 7 / 2 donne-t-il 3 en C# ?
Quand les deux opérandes sont des entiers, / effectue une division entière et abandonne la partie fractionnaire, en tronquant vers zéro. Faites de l'un des opérandes une valeur à virgule flottante ou décimale pour obtenir un résultat fractionnaire : 7 / 2.0 vaut 3,5, (double)a / b convertit avant de diviser, et 7m / 2 vaut 3,5 en decimal.
Quelle est la différence entre && et & en C# ?
&& est le ET conditionnel : si la partie gauche vaut false, la partie droite n'est pas évaluée. & sur des booléens évalue toujours les deux côtés, et sur des entiers c'est le ET bit à bit. Utilisez && et || pour les conditions, surtout pour les gardes comme user != null && user.IsActive, qui lèverait une NullReferenceException avec &.
Quelle est la différence entre i++ et ++i en C# ?
Les deux ajoutent 1 à i. L'expression i++ vaut l'ancienne valeur, ++i la nouvelle. Seule sur une ligne (i++;), il n'y a aucune différence. Elle ne compte que lorsque le résultat est utilisé, comme dans int a = i++;, c'est pourquoi beaucoup de guides de style gardent les incrémentations sur leur propre ligne.
Quelle est la priorité des opérateurs en C# ?
De la plus haute à la plus basse : primaires (x.y, f(x), a[i], x++), unaires (!, -, ++x, casts), multiplicatifs (* / %), additifs (+ -), décalages, relationnels (< > <= >= is as), égalité (== !=), &, ^, |, &&, ||, ??, le conditionnel ?: et enfin l'affectation. En cas de doute, ajoutez des parenthèses.