Ein Operator nimmt einen, zwei oder drei Operanden und erzeugt einen Wert: a + b, !done, x ?? fallback. Die Operatoren von C# ähneln denen von C, Java und JavaScript, aber einige verhalten sich überraschend für Einsteiger, allen voran die Ganzzahldivision und das Vorzeichen von %.
Arithmetische Operatoren
+, -, *, / und % funktionieren mit jedem numerischen Typ. Der Typ des Ergebnisses folgt den Operanden: Zwei int-Werte ergeben ein int, ein int und ein double ergeben ein double.
Ausgabe:
22
12
85
3
2
3.4
3.4
3
3.4
Die Ganzzahldivision schneidet in Richtung null ab. 17 / 5 ist 3 und -17 / 5 ist -3. Das verursacht einen sehr häufigen Bug: int percent = done / total * 100; ist 0, solange done < total gilt, weil done / total bereits 0 ist. Multipliziere zuerst (done * 100 / total) oder dividiere als double.
Eine Ganzzahl oder ein decimal durch null zu teilen wirft DivideByZeroException. Ein double durch null zu teilen wirft nichts; es ergibt Unendlich, oder NaN bei 0.0 / 0.0:
Ausgabe:
True
True
False
DivideByZeroException
Der Modulo-Operator und negative Zahlen
% gibt den Rest der Division zurück, und das Ergebnis übernimmt das Vorzeichen des linken Operanden. Das unterscheidet sich von Python und vom mathematischen „mod“, die bei positivem Divisor nie negativ sind:
Ausgabe:
1
-1
1
2
even
62 min 5 s
Thu
1.5
Nimm die Hilfsmethode Mod, wann immer die linke Seite negativ sein kann und das Ergebnis ein Index ist: days[(today + offset) % 7] würde bei einer negativen Summe eine IndexOutOfRangeException werfen. Der Test auf ungerade Zahlen hat dieselbe Falle: n % 2 == 1 ist für negative ungerade Zahlen false; prüfe n % 2 != 0.
Zuweisung, zusammengesetzte Zuweisung und Inkrement
= weist zu. Die zusammengesetzten Formen +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= verbinden eine Operation mit einer Zuweisung. ++ und -- addieren oder subtrahieren 1 und gibt es als Präfix- und Postfixform, die sich im zurückgegebenen Wert unterscheiden:
Ausgabe:
14
start, step
a=5 b=7 i=7
4
Der Fall mit byte zeigt eine Eigenheit: level = level + 10; kompiliert nicht, weil byte + int ein int ist, aber level += 10; kompiliert, weil die zusammengesetzte Zuweisung einen impliziten Cast zurück in den Typ der Variable enthält. Dieser Cast springt bei einem Überlauf um (aus 250 + 10 wird 4).
Vergleichsoperatoren und logische Operatoren
Vergleichsoperatoren (==, !=, <, >, <=, >=) geben ein bool zurück. Bei Strings vergleicht == den Text, nicht die Referenzen. Die logischen Operatoren sind ! (nicht), && (und), || (oder) und ^ (exklusives Oder).
Ausgabe:
True
no active user
with ||:
evaluated A
with |:
evaluated A
evaluated B
True
&& und || werten kurzgeschlossen aus: Der rechte Operand läuft nur, wenn er das Ergebnis ändern kann. Genau das macht user != null && user.IsActive sicher. Die einfachen & und | werten bei Booleans immer beide Seiten aus, was in einer Bedingung selten gewollt ist.
Bitweise Operatoren und Verschiebeoperatoren
Bei Ganzzahltypen arbeiten &, |, ^ und ~ auf einzelnen Bits, und << und >> verschieben sie. Sie werden für Flags, Masken, Hashing und Low-Level-Protokolle verwendet:
Ausgabe:
3
True
False
101
1024
125
5
-1
Für benannte Mengen von Flags ist ein Enum mit dem Attribut [Flags] klarer als Ganzzahlkonstanten, und HasFlag liest sich besser als eine Maske. C# 11 hat >>> hinzugefügt, eine vorzeichenlose Rechtsverschiebung, die auch bei negativen Zahlen mit Nullen auffüllt.
Null-Operatoren
Für Werte, die null sein können, gibt es drei Operatoren:
Ausgabe:
Guest
0
5
a ?? b gibt a zurück, außer es ist null, dann b. a?.Member gibt null zurück, statt zu werfen, wenn a null ist. C# 8 hat a ??= b hinzugefügt, das a nur dann b zuweist, wenn a null ist. Ausführlich behandelt werden sie unter Nullable-Typen.
Rangfolge der Operatoren
Wenn ein Ausdruck Operatoren mischt, binden höhere Zeilen stärker. Operatoren in derselben Zeile gruppieren von links nach rechts, außer Zuweisung, ?? und ?:, die von rechts nach links gruppieren. (Die Operanden selbst werden immer von links nach rechts ausgewertet.)
| Kategorie | Operatoren |
|---|---|
| Primär | x.y, x?.y, f(x), a[i], x++, x--, new, typeof, nameof |
| Unär | +x, -x, !x, ~x, ++x, --x, (T)x, await |
| Multiplikativ | *, /, % |
| Additiv | +, - |
| Verschiebung | <<, >>, >>> |
| Relational und Typprüfung | <, >, <=, >=, is, as |
| Gleichheit | ==, != |
| Bitweises UND | & |
| Bitweises XOR | ^ |
| Bitweises ODER | | |
| Bedingtes UND | && |
| Bedingtes ODER | || |
| Null-Sammeloperator | ?? |
| Bedingungsoperator | c ? a : b |
| Zuweisung und Lambda | =, +=, -=, ??=, => und die anderen zusammengesetzten Formen |
Zwei Folgen, die du kennen solltest: a + b * c multipliziert zuerst, und x & mask == 0 vergleicht zuerst (== bindet stärker als &), bedeutet also x & (mask == 0) und kompiliert für Ganzzahlen nicht. Schreibe (x & mask) == 0. Klammern kosten nichts und machen die Absicht offensichtlich.
Operatorüberladung
Deine eigenen Typen können festlegen, was Operatoren bedeuten. So unterstützen decimal, DateTime und TimeSpan die Operatoren + und <:
Ausgabe:
33.49
True
Operatoren müssen public static sein, und manche gibt es nur als Pflichtpaar: Wer < definiert, muss auch > definieren, und zu == gehört !=. Ist == definiert, warnt der Compiler außerdem, bis du Equals und GetHashCode passend überschreibst. Überlade Operatoren nur dort, wo die Bedeutung offensichtlich ist, etwa bei Zahlen, Vektoren und Geld.
Häufig gestellte Fragen
Was macht % in C#?
% ist der Restoperator: 17 % 5 ergibt 2. Das Ergebnis hat das Vorzeichen des linken Operanden, also ist -7 % 3 gleich -1 und nicht 2 wie in Python. Für ein Ergebnis, das nie negativ ist, wie du es beim Umlaufen eines Index brauchst, schreibe ((a % n) + n) % n. Er funktioniert auch mit double und decimal: 7.5 % 2 ist 1.5.
Warum ergibt 7 / 2 in C# 3?
Wenn beide Operanden Ganzzahlen sind, führt / eine Ganzzahldivision aus und verwirft den Nachkommaanteil, abgeschnitten in Richtung null. Mach einen Operanden zu einem Gleitkomma- oder Decimal-Wert, um ein Ergebnis mit Nachkommastellen zu bekommen: 7 / 2.0 ist 3.5, (double)a / b wandelt vor der Division um, und 7m / 2 ist 3.5 als decimal.
Was ist der Unterschied zwischen && und & in C#?
&& ist das bedingte UND: Ist die linke Seite false, wird die rechte nicht ausgewertet. & wertet bei Booleans immer beide Seiten aus und ist bei Ganzzahlen das bitweise UND. Nimm && und || für Bedingungen, besonders für Schutzabfragen wie user != null && user.IsActive, die mit & eine NullReferenceException werfen würden.
Was ist der Unterschied zwischen i++ und ++i in C#?
Beide addieren 1 zu i. Der Ausdruck i++ ergibt den alten Wert, ++i den neuen. Allein in einer Zeile (i++;) gibt es keinen Unterschied. Er zählt nur, wenn das Ergebnis verwendet wird, etwa in int a = i++;. Deshalb stellen viele Styleguides Inkremente in eine eigene Zeile.
Wie ist die Rangfolge der Operatoren in C#?
Von der höchsten zur niedrigsten: primär (x.y, f(x), a[i], x++), unär (!, -, ++x, Casts), multiplikativ (* / %), additiv (+ -), Verschiebungen, relational (< > <= >= is as), Gleichheit (== !=), &, ^, |, &&, ||, ??, der Bedingungsoperator ?: und zuletzt die Zuweisung. Im Zweifel setze Klammern.