Verzweigter Kontrollfluss

I. Grundlegende if / if-else Verzweigungen

  1. In den Klammern von if muss stets ein boolescher Ausdruck stehen; eine implizite Umwandlung zum Boolean ist nicht erlaubt.
  2. Mehrere else if-Blöcke können hintereinander geschrieben werden, optional folgt am Ende ein else.
  3. Bei einzeiligem Code lassen sich geschweifte Klammern weglassen, zur besseren Lesbarkeit empfiehlt es sich, sie durchgängig zu setzen.
void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Regenmantel mitnehmen");
  } else if (isSnowing) {
    print("Dickere Jacke anziehen");
  } else {
    print("Cabrio fahren");
  }
}Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Dickere Jacke anziehenCode-Sprache: CSS (css)

Man kann if auch alleine verwenden

void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Regenmantel mitnehmen");
  }
}Code-Sprache: Dart (dart)

Oder nur if else schreiben

void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Regenmantel mitnehmen");
  } else {
    print("Cabrio fahren");
  }
}Code-Sprache: Dart (dart)

II. if-case Musterabgleich-Verzweigung (ab Dart 3.0)

  1. Passt das Muster, werden Variablen per Destrukturierung an den aktuellen Gültigkeitsbereich gebunden.
  2. Schlägt der Abgleich fehl, springt der Code zu else. Mit when-Guard lassen sich zusätzliche boolesche Prüfungen ergänzen.
  3. Geeignet nur für ein Wert, ein Muster. Nutze switch bei mehreren zu prüfenden Mustern.
void main() {
  final pair = [100, 220];
  // Passt zu einer Liste mit zwei Ganzzahlen
  if (pair case [int x, int y]) {
    print("Koordinate: $x,$y");
  } else {
    throw FormatException("Ungültige Koordinate");
  }
}

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Koordinate: 10,20

2: if-case mit when-Guard

void main() {
  final data = [8, 9];
  if (data case [int a, int b] when a > 5 && b > 5) {
    print("Beide Zahlen sind größer als 5");
  } else {
    print("Zahlen erfüllen die Bedingung nicht");
  }
}

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Beide Zahlen sind größer als 5

III. switch-Anweisung

Klassische switch-Anweisung als Statement ohne Rückgabewert

  1. Cases unterstützen beliebige Muster: Zeichenketten, Konstanten, logisches ODER, Relationsmuster
  2. Nicht leere Cases brechen automatisch ab, man braucht kein manuelles break; leere Cases führen automatisch zum Fallthrough.
  3. Zwei Möglichkeiten für manuellen Fallthrough: continue Label oder aufeinanderfolgende leere Cases
  4. default wird ausgeführt, wenn kein Case passt; unterstützt ebenfalls when-Guards
1: Einfacher Zeichenkettenabgleich
void main() {
  String command = "OPEN";
  switch (command) {
    case "CLOSED":
      print("Schließlogik ausführen");
    case "PENDING":
      print("Wartelogik ausführen");
    case "OPEN":
      print("Öffnungslogik ausführen");
    default:
      print("Unbekannter Befehl");
  }
}

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Öffnungslogik ausführen

2: Fallthrough mit leerem Case + Sprung per continue-Label

String command = "OPEN";
switch (command) {
  case "OPEN":
    print("Öffnungsvorgang"); // ① Passt zu OPEN und gibt Text aus
    continue newCase; // ② Springt direkt zum Label newCase, kehrt nicht zurück

  case "DENIED": 
  case "CLOSED":
    print("Schließvorgang");

  newCase: // ③ Ziel-Label für den Sprung
  case "PENDING":
    print("Einheitliche Logik für wartende Vorgänge"); // ④ Wird sofort ausgeführt ohne Prüfung command == "PENDING"
}

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Öffnungsvorgang
Einheitliche Logik für wartende Vorgänge

In diesem Beispiel springt continue newCase; zwar zum Label newCase, aber der nachfolgende case „PENDING“ passt doch gar nicht – warum wird print("Einheitliche Logik für wartende Vorgänge"); trotzdem ausgeführt?

Diese Frage stellen sich viele Leser

continue Label; gleicht den Wert nicht erneut ab, sondern springt direkt zum Anfang des mit dem Label markierten Case-Blocks, überspringt alle Bedingungsprüfungen und führt den Code im Block unverzüglich aus.

3: switch mit when-Guard

void main() {
  int score = 88;
  switch (score) {
    case int s when s >= 90:
      print("Sehr gut");
    case int s when s >= 60:
      print("Bestanden");
    default:
      print("Nicht bestanden");
  }
}

Ausgabe: Bestanden

Zuerst wird der Typ geprüft – der Wert muss eine Ganzzahl sein. Passt der Typ, wird der Wert an Variable s übergeben. Danach läuft der when-Guard als zweite Prüfung; der Case-Code wird nur ausgeführt, wenn s die Bedingung nach when erfüllt. Die beiden int s haben separate Gültigkeitsbereiche und stören sich nicht gegenseitig.

Jedes case int s ist eine eigenständige Musterbindung, deren Gültigkeitsbereich ausschließlich auf den eigenen Case-Block beschränkt ist.

Man kann für beide Cases völlig unterschiedliche Variablennamen verwenden, das Verhalten bleibt identisch.

switch (score) {
  case int a when a >= 90:
    print("Sehr gut");
  case int b when b >= 60:
    print("Bestanden");
  default:
    print("Nicht bestanden");
}

IV. switch-Ausdruck

Syntax ab Dart 3.0 verfügbar, hat einen Rückgabewert und wird häufig zur Variablenzuweisung genutzt

Unterschiede zur switch-Anweisung

  1. Kein Schlüsselwort case, Muster und Rückgabeausdruck werden mit => getrennt
  2. Jeder Zweig muss einen Ausdruck zurückgeben, Zweige werden mit Kommas voneinander abgegrenzt
  3. Kein impliziter Fallthrough, jeder Zweig ist unabhängig. Passt kein Muster, nutze den Unterstrich _ statt default
  4. Direkt nutzbar bei Zuweisungen, print-Aufrufen, return und allen anderen Ausdruckskontexten

1: Einfacher Zeichenoperatorabgleich (logisches ODER-Muster)

void main() {
  const slash = 47, star = 42, comma = 44;
  int charCode = 42;

  final token = switch (charCode) {
    slash || star => "Operator",
    comma => "Satzzeichen",
    _ => "Ungültiges Zeichen"
  };
  print(token);
}

Ausgabeergebnis

Operator

Dieses Beispiel definiert Konstanten für die ASCII-Codes von drei Symbolen und prüft anhand der Variable charCode, ob es sich um einen Operator, Satzzeichen oder ungültiges Symbol handelt.

2: switch-Ausdruck mit when-Guard

void main() {
  int age = 22;
  String level = switch (age) {
    int a when a < 18 => "Minderjährig",
    int a when a < 30 => "Jugendlicher Erwachsener",
    _ => "Erwachsen"
  };
  print(level);
}

Ausgabeergebnis

Jugendlicher Erwachsener

3: Direkte Nutzung des switch-Ausdrucks im Funktionsreturn

void main() {
  print(getDesc(3));
}

String getDesc(int num) => switch (num) {
  1 => "Eins",
  2 => "Zwei",
  _ => "Andere Zahl"
};

Ausgabeergebnis

Andere Zahl

Hinweis: Hier handelt es sich bei switch um einen Ausdruck, getDesc ist eine Pfeilfunktion. Pfeilfunktionen sind eine Dart-eigene Syntax, die es in anderen C-ähnlichen Sprachen nicht gibt.

V. Vollständigkeitsprüfung (Exhaustiveness Checking)

Fehler zur Kompilierzeit, falls für mögliche Eingabewerte kein passender Case existiert – der Compiler zwingt zur Ergänzung fehlender Cases. Versiegelte Klassen (sealed) und Enums unterstützen die Vollständigkeitsprüfung nativ ohne _ oder default als Fallback.

1: Fehler bei unvollständigem nullable Boolean (Kommentiere den Code aus und führe ihn zur Fehleransicht aus)

void main() {
  bool? nullableBool = null;
  // Auskommentieren des unteren Codes erzeugt einen Kompilierfehler: Fehlender Case für null und kein Fallback durch _/default
  /*
  switch (nullableBool) {
    case true:
      print("Wahr");
    case false:
      print("Falsch");
  }
  */
  // Korrektur: Hinzufügen eines null-Cases oder Fallback mit _
  switch (nullableBool) {
    case true:
      print("Wahr");
    case false:
      print("Falsch");
    case null:
      print("Null-Wert");
  }
}

Ausgabeergebnis

Null-Wert

2: Sealed versiegelte Klasse

Algebraischer Datentyp, der eine lückenlose Musterabgleichung erzwingt

import 'dart:math' as math;

// Sealed-Klasse: Unterklassen dürfen nur in dieser Datei definiert werden
sealed class Shape {}

// Unterklasse Quadrat: Seitenlänge
class Square implements Shape {
  final double length;
  Square(this.length);
}

//Unterklasse Kreis: Radius
class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Pfeilfunktion, die per switch-Abgleich auf die übergebene Unterklasse die Fläche berechnet
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => math.pi * r * r
  // Hinzufügen einer neuen Unterklasse von Shape löst direkt einen Kompilierfehler aus, der auf fehlenden Case hinweist
};

void main() {
  final Shape circle = Circle(5);
  print(calculateArea(circle));
  final Shape square = Square(4);
  print(calculateArea(square));
}

Ausgabeergebnis

78.53981633974483
16.0

3: Lückenloser Abgleich mit Enum

Kein Fallback-Unterstrich _ erforderlich

void main() {
  final Animal animal = Animal.cat;
  String name = switch (animal) {
    Animal.dog => "Hund",
    Animal.cat => "Katze"
  };
  print(name);
}

enum Animal { dog, cat }

Ausgabe: Katze

Enums haben einen fest begrenzten Wertebereich, es können keine unbekannten Werte auftauchen – also braucht man keinen Fallback-Zweig.

VI. when-Guards

Vollständig unterstützt bei if-case, switch-Anweisungen und switch-Ausdrücken

Aufgabe von when: Zusätzliche boolesche Prüfung nach erfolgreichem Musterabgleich. Ist der Guard false, geht der Code zum nächsten Zweig, der switch-Block wird nicht verlassen.

1. if-case + when

void main() {
  List<int> arr = [10, 20];
  if (arr case [int x, int y] when x + y > 20) {
    print("Summe der beiden Zahlen ist größer als 20");
  }
}
Summe der beiden Zahlen ist größer als 20

Zuerst wird geprüft, ob es sich um eine Liste mit zwei Ganzzahlen handelt; passt das Muster, prüft when ob ihre Summe über 20 liegt.

2. switch-Anweisung + when

void main() {
  int num = 15;
  switch(num) {
    case int n when n % 2 == 0:
      print("Gerade Zahl");
    case int n when n % 2 != 0:
      print("Ungerade Zahl");
  }
}

Ausgabe: Ungerade Zahl

Zuerst wird der Typ als Ganzzahl bestätigt, danach läuft die Bedingung im when.

3. switch-Ausdruck + when

void main() {
  int num = 15;
  String res = switch(num) {
    int n when n % 2 == 0 => "Gerade Zahl",
    _ => "Ungerade Zahl"
  };
  print(res);
}

Verzweigter Kontrollfluss

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert