Zum Inhalt
wannacode

Exceptions

Kurz gesagt: Eine Exception ist ein Objekt, das einen Fehler beschreibt, der während des Programmlaufs passiert ist: Der Stacktrace zeigt, wo, und try und catch fangen ihn, damit das Programm weiterlaufen kann.

Problem

12a statt 12

Die Konzertkasse fragt, wie viele Tickets du willst. Ein Ticket kostet 15 €.

Program.cs
class Program
{
static void Main()
{
Console.Write("Wie viele Tickets? ");
int tickets = int.Parse(Console.ReadLine());
Console.WriteLine("Gesamt: " + tickets * 15 + " €");
}
}

Mit der Eingabe 12 sagt das Programm „Gesamt: 180 €“. Aber der Kunde hat es eilig und tippt einen Buchstaben zu viel:

Die Konsole
Wie viele Tickets? 12a
Unhandled exception. System.FormatException: The input string '12a' was not in a correct format.
at System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
at System.Int32.Parse(String s)
at Program.Main() in /projects/Tickets/Program.cs:line 6

Das Programm hält an. Statt der Summe kommt ein langer Text, und die Zeile „Gesamt“ wird nie erreicht. Was bedeutet dieser Text, und wie verhinderst du, dass das Programm wegen eines Buchstabens anhält?

Die Idee

Exception

int.Parse muss eine Zahl zurückgeben. Aus „12a“ kann es keine Zahl machen, und irgendeine Zahl zurückzugeben wäre gelogen. Deshalb erzeugt die Methode ein Objekt, das das Problem beschreibt, und wirft es. Dieses Objekt heißt Exception (Ausnahme).

Wenn niemand sie fängt, hält das Programm an und zeigt, was passiert ist. Dieser Text heißt Stacktrace, und du liest ihn von oben nach unten:

  • Die erste Zeile: die Art der Exception und die Meldung. FormatException: The input string '12a' was not in a correct format.: Die Zahl hat das falsche Format, und hier ist der Text.
  • Die Zeilen mit „at“: wo es passiert ist. Das ist der Aufrufstapel aus der Lektion über Rekursion, von innen nach außen. Ganz oben ist die Methode, die die Exception geworfen hat, und ganz unten ist Main.
  • Deine Zeile: die erste „at“-Zeile, die auf deine Datei zeigt. Hier: Program.cs:line 6. Die Zeilen darüber sind im Code von .NET, und du brauchst sie selten.

Die Meldungen sind auf Englisch, weil die Sprache sie schreibt, nicht du.

Probier es aus

Finde den Fehler

Das Programm soll das letzte Ergebnis aus der Klassenarbeit ausgeben. Es hält damit an:

Fehler
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Program.LastScore(Int32[] scores) in /projects/Tickets/Program.cs:line 5
at Program.Main() in /projects/Tickets/Program.cs:line 11

Lies den Stacktrace. In welcher Zeile des Programms steckt der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

try und catch

Eine Exception kann man fangen. Der Code, der werfen könnte, kommt in try („versuche“). Was bei einem Fehler passieren soll, schreibst du in catch („fange“):

Console.Write("Wie viele Tickets? ");
try
{
int tickets = int.Parse(Console.ReadLine());
Console.WriteLine("Gesamt: " + tickets * 15 + " €");
}
catch (FormatException)
{
Console.WriteLine("Das ist keine ganze Zahl.");
}
Console.WriteLine("Vielen Dank!");
Eingabe
12a
Ausgabe
Wie viele Tickets? Das ist keine ganze Zahl.
Vielen Dank!
  • In die Klammern von catch schreibst du, welche Art von Exception du fängst.
  • Wenn im try alles gut geht, wird catch übersprungen. Mit der Eingabe 12 ist die Ausgabe „Gesamt: 180 €“ und „Vielen Dank!“.
  • Nach catch macht das Programm weiter. Es hält nicht mehr an.

Console.Write("Wie viele Tickets? ");
try
{
int tickets = int.Parse(Console.ReadLine());
Console.WriteLine("Gesamt: " + tickets * 15 + " €");
}
catch (FormatException)
{
Console.WriteLine("Das ist keine ganze Zahl.");
}
Console.WriteLine("Vielen Dank!");
Schritt für Schritt, in Worten
  1. Das Programm fragt, wie viele Tickets. Du tippst „12a“.
  2. Im try: int.Parse versucht, aus „12a“ eine Zahl zu machen.
  3. Das geht nicht. Die Methode wirft FormatException.
  4. Die Ausführung springt direkt in catch. Die Zeile mit „Gesamt“ wird übersprungen.
  5. catch ist für genau diese Art von Fehler da: Es gibt die Meldung für den Kunden aus.
  6. Nach catch macht das Programm weiter. Es hält nicht an.
Genauer, für Neugierige
  • Eine Variable, die im try deklariert wird (hier tickets), existiert nur bis zur schließenden Klammer des try. Wenn du sie auch nach dem Block brauchst, deklariere sie vor dem try.
  • In C# kannst du die Exception ohne Namen lassen: catch (FormatException). Das machst du, wenn du sie nicht brauchst. Wenn du einen Namen schreibst und ihn nicht benutzt, gibt der Compiler eine Warnung.

Probier es aus

Welche Zeilen laufen?

Was gibt das Programm aus?

Console.WriteLine("Start");
try
{
Console.WriteLine("Ich lese");
int seats = int.Parse("zehn");
Console.WriteLine("Gelesen: " + seats);
}
catch (FormatException)
{
Console.WriteLine("Falsche Eingabe");
}
Console.WriteLine("Ende");
Antwort

Die Idee

Was in der Exception steht

Die Exception ist ein Objekt, und e in catch (FormatException e) ist die Variable, die darauf zeigt. Das Objekt kann dir die Meldung geben: e.Message.

try
{
int tickets = int.Parse(Console.ReadLine());
Console.WriteLine("Gesamt: " + tickets * 15 + " €");
}
catch (FormatException e)
{
Console.WriteLine("Fehler: " + e.Message);
}
Eingabe
12a
Ausgabe
Fehler: The input string '12a' was not in a correct format.

Die Meldung ist nützlich für dich, wenn du den Fehler suchst. Dem Kunden zeigst du meistens deinen eigenen Text in seiner Sprache, etwa „Das ist keine ganze Zahl.“

Die Idee

Frag, bis es stimmt

Der Kunde hat sich vertippt. Du musst nicht anhalten. Frag noch einmal:

int tickets;
while (true)
{
Console.Write("Wie viele Tickets? ");
try
{
tickets = int.Parse(Console.ReadLine());
break;
}
catch (FormatException)
{
Console.WriteLine("Gib eine ganze Zahl ein, zum Beispiel 3.");
}
}
Console.WriteLine("Gesamt: " + tickets * 15 + " €");
Eingabe
12a
zwölf
12
Ausgabe
Wie viele Tickets? Gib eine ganze Zahl ein, zum Beispiel 3.
Wie viele Tickets? Gib eine ganze Zahl ein, zum Beispiel 3.
Wie viele Tickets? Gesamt: 180 €

while (true) läuft immer weiter, bis break die Schleife beendet. Und break wird nur erreicht, wenn Parse Erfolg hat. Bei einem Fehler springt die Ausführung in catch, danach beginnt die Schleife von vorn und fragt noch einmal.

Genauer, für Neugierige
  • Wenn eine falsche Eingabe etwas Normales ist, hat C# einen bequemeren Weg: int.TryParse. Es wirft keine Exception. Es gibt true oder false zurück und schreibt die Zahl in den out-Parameter aus der Lektion über Parameter:
int tickets;
Console.Write("Wie viele Tickets? ");
while (!int.TryParse(Console.ReadLine(), out tickets))
{
Console.WriteLine("Gib eine ganze Zahl ein, zum Beispiel 3.");
Console.Write("Wie viele Tickets? ");
}
Console.WriteLine("Gesamt: " + tickets * 15 + " €");

Exceptions sind für Situationen, die du nicht oft erwartest. Zum Prüfen einer Eingabe ist TryParse kürzer und schneller.

Probier es aus

Division durch null

Nicht jede Exception kommt von der Eingabe. Der durchschnittliche Bestellwert ist der Umsatz geteilt durch die Zahl der Bestellungen. Aber am ersten Tag gibt es keine Bestellungen. Mit ganzen Zahlen hält revenue / orders das Programm an:

Fehler
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.

Und mit Kommazahlen?

Was gibt das Programm aus?

int revenue = 90;
int orders = 0;
double average = (double)revenue / orders;
Console.WriteLine(average);
Antwort
Genauer, für Neugierige
  • Hier ist die Ausgabe Infinity. Auf einem Computer mit Regionaleinstellungen, zum Beispiel deutschen oder amerikanischen, gibt C# das Zeichen ∞ aus.
  • Ganze Zahlen haben keinen Wert für unendlich, deshalb bleibt dort nur die Exception. Für Kommazahlen kommen die Regeln aus dem Standard IEEE 754 für Gleitkommazahlen, dem alle Sprachen folgen.

Übung

Programme, die nicht anhalten

Löse die Aufgaben in deinem Editor. Gib keine Eingabeaufforderungen aus, damit deine Ausgabe mit dem Beispiel übereinstimmt.

1. Text zu Zahl. Schreibe eine Methode ToNumber(string text), die den Text als double zurückgibt. Lies zwei Zeilen. Gib für jede die Zahl aus oder eine Meldung, dass es keine Zahl ist. Fange die Exception in Main.

Eingabe
123.45
123.abc
Ausgabe
Die Zahl ist: 123.45
Fehler: 123.abc ist keine Zahl.

Auf einem Computer mit deutschen Regionaleinstellungen will double.Parse ein Komma: 123,45. Siehe die Lektion „Eingabe und Ausgabe“.

2. Kein solches Produkt. Die Preise stehen im Array { 120, 75, 300, 45, 89 }. Lies eine Produktnummer von 1 bis 5 und gib ihren Preis aus. Wenn die Nummer außerhalb des Arrays liegt, fange die Exception und sag es. Mit der Eingabe 2 ist die Ausgabe Preis: 75 €.

Eingabe
7
Ausgabe
Es gibt kein Produkt mit der Nummer 7.

3. Durchschnittliche Bestellung. Lies den Umsatz und die Zahl der Bestellungen (ganze Zahlen) und gib den Durchschnitt mit Ganzzahldivision aus. Wenn es keine Bestellungen gibt, fange die Exception. Mit der Eingabe 900 und 4 ist die Ausgabe Durchschnitt: 225 €.

Eingabe
90
0
Ausgabe
Noch keine Bestellungen.

Weitere Aufgaben

  • Alter, bis es stimmt. Frage nach dem Alter, bis die Eingabe eine ganze Zahl von 0 bis 120 ist. Bei Text wie „zwanzig“ sag „Das ist keine Zahl.“, und bei 150 sag „Das Alter muss zwischen 0 und 120 liegen.“ Das eine ist eine Exception, das andere ein if.
  • Summe der gültigen. Lies 5 Zeilen. Addiere die, die ganze Zahlen sind, und zähle die übersprungenen: Mit der Eingabe 10, abc, 5, 7.5, 3 ist die Ausgabe Summe: 18, übersprungen: 2.
  • Division auf Wunsch. Lies zwei ganze Zahlen und gib den Quotienten aus. Bei Text statt einer Zahl und bei Division durch null hält das Programm nicht an. Es fragt noch einmal nach beiden Zahlen.
  • Ohne Exception. Löse Aufgabe 1 mit double.TryParse statt mit try/catch.

Test

Teste dich

1. Was ist eine Exception?
2. Das Programm ist mit einem langen Fehlertext angehalten. Wo fängst du an zu lesen?
3. In einem try stehen vier Zeilen. Die zweite wirft eine Exception, und catch fängt sie. Was passiert mit der dritten und der vierten?
4. Was passiert bei int x = 90 / orders; bei orders = 0?
5. Fängt catch (FormatException e) den Fehler von scores[10] in einem Array mit 5 Elementen?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·