Menu

C# Ternärer Operator (?:): Syntax, Typen, Verschachtelung und ??

Der Bedingungsoperator condition ? a : b wählt in C# einen von zwei Werten. Wie er ausgewertet wird, warum beide Zweige einen gemeinsamen Typ brauchen (CS0173) und welcher Cast das behebt, verschachtelte ternäre Ausdrücke, Klammern in der String-Interpolation und wann ?? und ?. es besser sagen.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Der Bedingungsoperator, meist ternärer Operator genannt, wählt in einem einzigen Ausdruck zwischen zwei Werten: condition ? valueIfTrue : valueIfFalse.

Ausgabe:

0 items, shipping 4.99
1 item, shipping 4.99
5 items, shipping 0

Die Bedingung muss ein bool sein. Nach ihrer Auswertung läuft nur einer der beiden Zweige; der andere wird nie ausgewertet. Das macht den Operator sicher für abgesicherte Zugriffe wie list.Count > 0 ? list[0] : "empty", wo die Auswertung von list[0] bei einer leeren Liste werfen würde.

Ternärer Operator gegenüber if/else

Der ternäre Operator ist ein Ausdruck: Er erzeugt einen Wert, den du zuweist, übergibst oder zurückgibst. Eine if-Anweisung führt Anweisungen aus. Diese beiden Methoden tun dasselbe:

// with if/else
static string Status(int stock)
{
    if (stock > 0)
        return "in stock";
    else
        return "sold out";
}

// with the ternary
static string Status(int stock) => stock > 0 ? "in stock" : "sold out";

Nimm den ternären Operator, wenn beide Zweige kurze Werte derselben Art sind. Nimm if, wenn ein Zweig mehrere Anweisungen ausführt, Nebeneffekte hat oder die Bedingung so lang ist, dass die ganze Zeile nicht mehr bequem passt.

Der ternäre Operator kann kein if ersetzen, das nur Aktionen ausführt. Das hier kompiliert nicht:

stock > 0 ? Ship(order) : Refund(order);
// error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

Es gibt auch keinen ternären Operator ohne else: ? und : sind immer Pflicht, weil der Ausdruck in beiden Fällen einen Wert haben muss.

Beide Zweige brauchen einen gemeinsamen Typ

Der Compiler ermittelt den Typ des ganzen Ausdrucks aus seinen beiden Zweigen. Ein Zweig muss sich implizit in den Typ des anderen umwandeln lassen. Geht das in keine Richtung, bekommst du CS0173:

var discount = isMember ? 10 : null;
// error CS0173: Type of conditional expression cannot be determined because
// there is no implicit conversion between 'int' and '<null>'

var label = inStock ? 1 : "none";
// error CS0173: ... between 'int' and 'string'

Die Lösung ist, einen Zweig in den gewünschten Typ zu casten, damit sich der andere dorthin umwandeln lässt:

Ausgabe:

no discount
7
3.5

points / 2.0 : 0 braucht keinen Cast: 0 ist ein int, das sich implizit in double umwandeln lässt, also ist der Ausdruck ein double.

C# 9 hat Zieltypisierung hinzugefügt: Wird der Ausdruck einem deklarierten Typ zugewiesen, verwendet der Compiler diesen Typ. int? discount = isMember ? 10 : null; kompiliert ab C# 9. Mit var gibt es keinen Zieltyp, var discount = isMember ? 10 : null; scheitert also in jeder Version.

Rangfolge und Klammern

Der Bedingungsoperator hat eine sehr niedrige Rangfolge, niedriger als +, == und &&. Das ist praktisch in a > b ? a : b und überraschend innerhalb einer String-Verkettung:

Console.WriteLine("Status: " + isActive ? "on" : "off");
// error CS0029: Cannot implicitly convert type 'string' to 'bool'

Der Compiler liest die Bedingung als "Status: " + isActive, also als String. Klammern um den ternären Ausdruck beheben das: "Status: " + (isActive ? "on" : "off").

Die String-Interpolation hat ihre eigene Falle. Innerhalb von { } leitet ein Doppelpunkt einen Formatbezeichner ein ({price:F2}), ein nackter ternärer Ausdruck verwirrt also den Parser. Setze ihn in Klammern:

Ausgabe:

2 users online
You have 1 unread message
Status: online

Verschachtelte ternäre Operatoren

Der Operator ist rechtsassoziativ, a ? x : b ? y : z gruppiert sich also als a ? x : (b ? y : z). Eine kleine Kette mit einer Bedingung pro Zeile ist lesbar:

Ausgabe:

512 B
20 KB
5 MB

Ab zwei oder drei Ebenen oder wenn die Bedingungen verschiedene Dinge prüfen, wechsle zu if/else if oder einem switch-Ausdruck. Die Verschachtelung im true-Zweig (a ? (b ? x : y) : z) ist die Variante, die zuerst schwer nachzuvollziehen wird.

?? und ?. ersetzen häufige ternäre Ausdrücke

Viele ternäre Ausdrücke existieren nur, um null zu behandeln. Dafür hat C# kürzere Operatoren:

Ternärer AusdruckKürzere FormBedeutung
name != null ? name : "guest"name ?? "guest"Ersatzwert bei null
customer != null ? customer.Email : nullcustomer?.EmailMember-Zugriff, der null toleriert
c != null && c.Address != null ? c.Address.City : "unknown"c?.Address?.City ?? "unknown"beides kombiniert
value != null ? value : throw new ArgumentNullException(...)value ?? throw new ArgumentNullException(...)früh scheitern

Ausgabe:

Lena from Berlin
guest from unknown
guest from unknown

Ein throw-Ausdruck ist auch als einer der beiden Zweige eines ternären Operators erlaubt: int age = input >= 0 ? input : throw new ArgumentOutOfRangeException(nameof(input));.

Der ref-Bedingungsausdruck

Seit C# 7.2 kann ein ternärer Ausdruck zwischen zwei Variablen statt zwei Werten wählen, und du kannst über das Ergebnis zuweisen:

// C# 7.2 and later
int wins = 0, losses = 0;
bool won = true;

ref int counter = ref (won ? ref wins : ref losses);
counter++;               // increments wins

Beide Zweige müssen ref auf Variablen desselben Typs sein. In Anwendungscode ist das selten; es existiert vor allem für performancekritischen Code, der mit großen Structs oder Arrays arbeitet, ohne sie zu kopieren.

Häufig gestellte Fragen

Was ist der ternäre Operator in C#?

Es ist der Bedingungsoperator condition ? valueIfTrue : valueIfFalse, der einzige C#-Operator mit drei Operanden. Er wertet die Bedingung aus und wertet dann genau einen der beiden Werte aus und gibt ihn zurück: string label = age >= 18 ? "adult" : "minor";.

Kann ich in C# einen ternären Operator ohne else verwenden?

Nein. Sowohl der Teil mit ? als auch der mit : sind Pflicht, weil der Ausdruck immer einen Wert erzeugen muss. Wenn du nur etwas tun willst, wenn eine Bedingung gilt, nimm eine if-Anweisung. Der ternäre Operator kann auch nicht allein als Anweisung stehen: x > 0 ? A() : B(); ist Fehler CS0201.

Warum bekomme ich „Type of conditional expression cannot be determined“?

Die beiden Zweige haben keinen gemeinsamen Typ, zum Beispiel var x = found ? 42 : null; (int und null) oder found ? 1 : "none". Caste einen Zweig, damit ein Typ existiert: found ? (int?)42 : null. Seit C# 9 funktioniert auch ein deklarierter Zieltyp: int? x = found ? 42 : null; kompiliert, var scheitert aber weiterhin.

Wie verwende ich den ternären Operator in der String-Interpolation?

Setze ihn in Klammern: $"{(count == 1 ? "item" : "items")}". Ohne Klammern wird der : als Beginn eines Formatbezeichners gelesen, und der Code kompiliert nicht.

Kann man ternäre Operatoren in C# verschachteln?

Ja. Der Operator ist rechtsassoziativ, a ? x : b ? y : z bedeutet also a ? x : (b ? y : z). Eine Verschachtelungsebene, auf separate Zeilen verteilt, liest sich gut; tiefere Ketten sind als if/else if-Kette oder switch-Ausdruck klarer.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S