Ausnahme- und Fehlerbehandlung

Alle Ausnahmen in Dart sind ungeprüfte Ausnahmen. Man muss in Funktionen nicht deklarieren, welche Typen geworfen werden, und das Abfangen ist nicht verpflichtend.

Es gibt zwei Arten:

  • Exception:Vorhersehbare Fehler der Geschäftslogik (Formatprobleme, ungültige Parameter usw., das Abfangen und Behandeln wird empfohlen)
  • Error:Fatale Programmfehler (Typfehler, Nullpointer, nicht implementierte Methoden usw., normalerweise nicht abgefangen)

Jedes nicht-null Objekt kann mit throw geworfen werden. Bei regelkonformen Projekten werden nur Unterklassen von Exception/Error geworfen.

Nicht abgefangene Ausnahmen beenden die aktuelle Isolate und führen zum Programmabsturz.

throw – Ausnahmen werfen

1. Systemeigene Ausnahmen werfen

void main() {
  // Format-Ausnahme
  throw FormatException("Es ist mindestens ein Datensegment erforderlich");
}
Code-Sprache: Dart (dart)
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
FormatException: Fehlerbeschreibung
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:3:3)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code-Sprache: PHP (php)

Dieser Code wirft eine Ausnahme mit eigener Beschreibung und gibt beim Ausführen einen Fehler aus. Vielleicht fragt man sich, wofür dieser Code nützlich ist – das Werfen von Ausnahmen ist aber sehr wichtig. Zum Beispiel bei der Benutzerregistrierung validieren wir die Eingaben. Sind diese nicht regelkonform, werfen wir sofort eine Ausnahme, um den Benutzer auf den Fehler hinzuweisen, den Ablauf abzubrechen und fehlerhafte Daten zu blockieren, damit die Datenintegrität gewahrt bleibt.

2. Beliebige Objekte werfen

Nicht für Produktivumgebungen empfohlen

void main() {
  throw "Textbeschreibung!";
}Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
Textbeschreibung!
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:3)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code-Sprache: PHP (php)

3. throw in Pfeilfunktionen verwenden (throw ist ein Ausdruck)

class Point {}
void distanceTo(Point other) => throw UnimplementedError("Diese Methode ist nicht implementiert");

void main() {
  distanceTo(Point());
}
Code-Sprache: JavaScript (javascript)

III. try /on/catch – Ausnahmen abfangen

Syntaxunterschiede

  • on AusnahmeTyp:Passt nur auf Ausnahmen des angegebenen Typs, das Ausnahmeobjekt kann nicht abgerufen werden
  • catch(e):Fängt alle Ausnahmen ab, die Variable e enthält die Ausnahmeinstanz
  • catch(e, s):e = Ausnahmeobjekt, s = StackTrace-Aufrufinformationen

1: Einzelnen Ausnahmetyp mit on abfangen


// Eigene Ausnahmeklasse erstellen, die Exception implementiert
class OutOfLlamasException implements Exception {}

// Funktion zum Werfen der obigen eigenen Ausnahme
void breedMoreLlamas() {
  throw OutOfLlamasException();
}

void buyMoreLlamas() {
  print("buyMoreLlamas wurde ausgeführt");
}

void main() {
  try {
    breedMoreLlamas(); // Funktion aufrufen und ausführen
  } on OutOfLlamasException {
    buyMoreLlamas(); // Wird ausgeführt, falls OutOfLlamasException abgefangen wird
  }
}Code-Sprache: Dart (dart)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
buyMoreLlamas wurde ausgeführtCode-Sprache: CSS (css)
try  {
    // Code, bei dem Fehler auftreten können
} on AusnahmeKlasse {
   // Code, der bei abgefangenem Fehler ausgeführt wird
}Code-Sprache: Dart (dart)

try on ist lediglich eine Syntax zum typbasierten Abfangen von Ausnahmen.

2: Mehrzweigige hierarchische Abfanglogik

Genauere Treffer werden zuerst ausgewertet

class OutOfLlamasException implements Exception {}

void breedMoreLlamas() {
  throw FormatException("Datenformatfehler");
}

void main() {
  try {
    breedMoreLlamas();
  }
  // 1. Exakter Treffer für eigene Ausnahme
  on OutOfLlamasException {
    print("Es ist ein OutOfLlamasException-Fehler aufgetreten");
  }
  // 2. Alle Unterklassen von Exception abfangen
  on Exception catch (e) {
    print("Allgemeiner Geschäftsausnahme: $e");
  }
  // 3. Fallback für Errors und beliebige andere Objekte
  catch (e) {
    print("Unbekannter schwerwiegender Fehler: $e");
  }
}
Code-Sprache: Dart (dart)

Ausgabe:Allgemeiner Geschäftsausnahme: FormatException: Datenformatfehler

Man kann mehrere on-Blöcke nutzen, um einzelne Exception-Typen gezielt abzufangen, und mit catch als universellen Fallback ergänzen.

3: Ausnahme abfangen + StackTrace-Aufrufbaum ausgeben

void main() {
  try {
    int a = null;
    print(a + 1);
  } catch (e, stack) {
    print("Ausnahmeinformation: $e");
    print("Vollständiger Aufrufbaum:\n$stack");
  }
}
Code-Sprache: Dart (dart)

Dieser Code gibt nicht nur die Fehlermeldung aus, sondern auch den gesamten Funktionsaufrufbaum zur Fehleranalyse.

D:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:3:13: Error: A value of type 'Null' can't be assigned to a variable of type 'int'.
    int a = null;Code-Sprache: PHP (php)

IV. rethrow

Teilweise behandeln und weiter nach oben werfen

Zweck: Die aktuelle Funktion bearbeitet einen Teil der Logik und gibt die Ausnahme anschließend an den aufrufenden Code zur weiteren Behandlung weiter.

Manchmal kann der Fehler nicht vollständig in der aktuellen Funktion abgearbeitet werden; man muss die Ausnahme an die aufrufende Ebene weitergeben, damit dort zusätzliche Maßnahmen ergriffen werden.

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // true unterstützt keine Inkrementierung, Laufzeitfehler
  } catch (e) {
    print("Lokale Behandlung in misbehave: ${e.runtimeType}");
    rethrow; // Erneut werfen, damit der äußere try-Block es abfängt
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print("Abschließende Behandlung in main: ${e.runtimeType}");
  }
}
Code-Sprache: Dart (dart)

Ausführungsablauf:

Lokale Behandlung in misbehave: NoSuchMethodError
Abschließende Behandlung in main: NoSuchMethodError

Der obige Code verarbeitet den Fehler sowohl in der Funktion misbehave als auch in main.

V. Der finally-Block

Wird unabhängig von Fehlern immer ausgeführt

Zwei Anwendungsfälle:

  1. Nur try + finally: Der Fehler wird nicht abgefangen, das Programm stürzt nach Ausführung von finally ab
  2. try + catch + finally: Zuerst den Fehler behandeln, dann finally ausführen (nur zur Ressourcenfreigabe)

1: try + finally

void cleanLlamaStalls() => print("Ressourcen bereinigen, obligatorisch auszuführender Sicherheitsblock");
void breedMoreLlamas() => throw Exception("Es ist ein Fehler aufgetreten");

void main() {
  try {
    breedMoreLlamas();
  } finally {
    cleanLlamaStalls();
  }
}
Code-Sprache: Dart (dart)

Der Bereinigungscode läuft zwar, aber die nicht abgefangene Ausnahme lässt das Programm am Ende abstürzen.

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Ressourcen bereinigen, obligatorisch auszuführender Sicherheitsblock
Unhandled exception:
Exception: Es ist ein Fehler aufgetreten
#0      breedMoreLlamas (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:27)
#1      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:5)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#3      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code-Sprache: PHP (php)

2: try + catch + finally

Standard-Syntax zur Ressourcenfreigabe, zum Beispiel zum Schließen geöffneter Dateien

void cleanLlamaStalls() => print("Dateien schließen / belegte Ressourcen freigeben");
void breedMoreLlamas() => throw FormatException("Parsing fehlgeschlagen");

void main() {
  try {
    breedMoreLlamas();
  } catch (e) {
    print("Ausnahme abgefangen: $e");
  } finally {
    cleanLlamaStalls();
  }
}
Code-Sprache: Dart (dart)

Ausführungsreihenfolge: Ausnahme tritt auf → catch behandelt den Fehler → finally wird ausgeführt

Ausgabeergebnis

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Ausnahme abgefangen: FormatException: Parsing fehlgeschlagen
Dateien schließen / belegte Ressourcen freigeben

VI. assert – Zusicherungen

Nur im Entwicklungs- und Debugmodus aktiv, in Produktversionen vollständig deaktiviert

assert(Bedingung, [optionaler Fehlertext])

Ist die Bedingung false, wird eine AssertionError geworfen. In Produktbuilds wird der Code vollständig ignoriert und verursacht keine Leistungseinbußen.

1: Einfache Zusicherung

void main() {
  String? text;
  int number = 120;
  String url = "http://foxdevelop.com";

  assert(text != null); // Nullprüfung
  assert(number < 100); // Zahlenbereichsprüfung
  assert(url.startsWith("https")); // URL-Protokoll prüfen
}
Code-Sprache: JavaScript (javascript)

Führt man diesen Code unverändert aus, wird nichts angezeigt!

Standardmäßig sind Zusicherungen deaktiviert; man muss sie über einen Startparameter aktivieren, siehe unten.

2: Eigenen Fehlertext definieren

void main() {
  String url = "http://test.com";
  assert(
    url.startsWith("https"),
    "Der Link $url muss mit https beginnen",
  );
}
Code-Sprache: JavaScript (javascript)

Man kann frei festlegen, welcher Text bei fehlschlagender Zusicherung angezeigt wird.

Aktivieren von assert

  1. Flutter Debug-Modus: Zusicherungen standardmäßig aktiv
  2. Ausführung per dart-Befehl: dart --enable-asserts Dateiname.dart
  3. Produktpaketierung: Sämtlicher assert-Code wird entfernt, keine Leistungseinbußen
D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code-Sprache: PHP (php)

Sowohl dart run als auch der einfache dart-Befehl deaktivieren assert standardmäßig. Man muss den Parameter --enable-asserts hinzufügen:

Ausführungsbefehl-Beispiel

dart run --enable-asserts main.dartCode-Sprache: CSS (css)

Befindet sich das Terminal im Projektstammordner, kann man die Kurzform verwenden:

Ausführungsbefehl:

dart run --enable-asserts

dart run:Direkte Ausführung von Dart-Quellcode für Entwicklung und Debugging

dart Dateiname:Vereinfachte Syntax für einzelne Dateien

dart compile exe:Kompilieren zu eigenständigen Binärdateien, assert ist im kompilierten Ergebnis stets deaktiviert

Die Dart-Dateien liegen im Unterordner bin des Projekts. Man kann entweder in bin navigieren und die Datei direkt ausführen oder eine Ebene höher im Projektstamm das gesamte Projekt starten.

dart run –enable-asserts

Unten sind beide Vorgehensweisen abgebildet: Ausführung direkt aus dem bin-Ordner und Ausführung aus dem übergeordneten Projektstamm.

D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)

D:\dartdemo\firstdart\bin>dart run
Building package executable...
Built firstdart:firstdart.

D:\dartdemo\firstdart\bin>cd ..

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code-Sprache: PHP (php)

Ist die Bedingung der Zusicherung true, wird keine Ausnahme geworfen.

Ändere den Beispielcode wie folgt: Ändere http in der URL zu https, ersetze text != null durch text == null und setze number auf 99.

void main() {
  String? text;
  int number = 99;
  String url = "https://foxdevelop.com";

  assert(text == null); // Nullprüfung
  assert(number < 100); // Zahlenbereichsprüfung
  assert(url.startsWith("https")); // URL-Protokoll prüfen
}Code-Sprache: Dart (dart)

Wird der Code erneut ausgeführt, treten keine Ausnahmen auf.

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.Code-Sprache: CSS (css)

VII. Vollständiges Beispiel

void cleanResource() => print("finally: belegte Ressourcen freigeben");

void testError() {
  try {
    int num = 150;
    assert(num < 100, "Die Zahl muss kleiner als 100 sein");
    throw FormatException("Ungültiges Datenformat");
  } on FormatException catch (e) {
    print("Formatausnahme abgefangen: $e");
    rethrow;
  } finally {
    cleanResource();
  }
}

void main() {
  try {
    testError();
  } catch (e) {
    print("main fängt die weitergeworfene Ausnahme ab: $e");
  }
}
Code-Sprache: PHP (php)

Ausgabeergebnis

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
finally: belegte Ressourcen freigeben
main fängt die weitergeworfene Ausnahme ab: 'file:///D:/dartdemo/firstdart/bin/firstdart.dart':
 Failed assertion: line 6 pos 12: 'num < 100': Die Zahl muss kleiner als 100 seinCode-Sprache: PHP (php)

Vollständiges Beispiel für den gesamten Ablauf der Ausnahme- und Fehlerbehandlung

Schreibe einen Kommentar

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