> ## Documentation Index
> Fetch the complete documentation index at: https://homelab.amylify.de/llms.txt
> Use this file to discover all available pages before exploring further.

# 4.2.7 Fehler & Dateien

> Fehlerbehandlung mit try und catch, Debugging und das Lesen und Schreiben von Dateien in C#.

Programme stoßen auf Unvorhergesehenes: eine fehlende Datei, eine falsche Eingabe, eine Division durch null. Dieses Kapitel zeigt, wie du damit umgehst, Fehler findest und Daten dauerhaft speicherst.

***

## Drei Arten von Fehlern

| Art | Wann er auftritt | Beispiel |
| :- | :- | :- |
| **Syntaxfehler** | Beim Kompilieren | Fehlendes Semikolon, Tippfehler im Namen |
| **Laufzeitfehler** | Während das Programm läuft | Datei nicht gefunden, Division durch 0 |
| **Logikfehler** | Programm läuft, Ergebnis ist falsch | Falsche Formel, `<` statt `<=` |

Syntaxfehler zeigt der Compiler sofort an. Laufzeitfehler heißen in C# **Exceptions** (Ausnahmen). Logikfehler findest du nur durch Testen und Debuggen.

***

## Exceptions behandeln

Eine Exception unterbricht das Programm, bis jemand sie **abfängt**. Dafür dient `try` und `catch`:

```csharp theme={"dark"}
try
{
    Console.Write("Zahl: ");
    int zahl = int.Parse(Console.ReadLine()!);
    int ergebnis = 100 / zahl;
    Console.WriteLine($"100 / {zahl} = {ergebnis}");
}
catch (FormatException)
{
    Console.WriteLine("Das war keine Zahl.");
}
catch (DivideByZeroException)
{
    Console.WriteLine("Durch null kann man nicht teilen.");
}
finally
{
    Console.WriteLine("Fertig.");   // läuft immer
}
```

| Block | Aufgabe |
| :- | :- |
| `try` | Code, der einen Fehler auslösen könnte |
| `catch (Typ)` | Reagiert auf einen bestimmten Fehlertyp. Es dürfen mehrere folgen |
| `finally` | Läuft **immer**, ob Fehler oder nicht. Gedacht zum Aufräumen |

Den Fehler selbst bekommst du über eine Variable:

```csharp theme={"dark"}
catch (Exception ex)
{
    Console.WriteLine($"Fehler: {ex.Message}");
}
```

> [!WARNING]
>
> Fange nur Fehler ab, die du auch sinnvoll behandeln kannst. Ein leeres `catch { }` versteckt Probleme und macht die Suche später sehr mühsam. Muss ein allgemeiner `catch (Exception)` sein, gib den Fehler mit `throw;` am Ende weiter.

### Häufige Exceptions

| Exception | Ursache |
| :- | :- |
| `FormatException` | Text lässt sich nicht in den Zieltyp umwandeln |
| `DivideByZeroException` | Ganzzahl durch 0 geteilt |
| `IndexOutOfRangeException` | Array-Index außerhalb des gültigen Bereichs |
| `KeyNotFoundException` | Schlüssel im `Dictionary` nicht vorhanden |
| `NullReferenceException` | Zugriff auf etwas, das `null` ist |
| `ArgumentException` | Ungültiges Argument an eine Methode übergeben |
| `FileNotFoundException` | Datei existiert nicht |
| `InvalidOperationException` | Aktion im aktuellen Zustand nicht erlaubt |

### Eigene Fehler auslösen

Mit `throw` meldest du selbst einen Fehler, wenn eine Methode mit unsinnigen Werten aufgerufen wird:

```csharp theme={"dark"}
static double Wurzel(double zahl)
{
    if (zahl < 0)
        throw new ArgumentException("Die Zahl darf nicht negativ sein.", nameof(zahl));

    return Math.Sqrt(zahl);
}
```

> [!TIP]
>
> Vermeide Exceptions für normale Abläufe. Ob eine Eingabe eine Zahl ist, prüfst du besser mit `int.TryParse`. Es ist schneller und braucht kein `try`.

***

## Debugging

Der **Debugger** hält das Programm an einer Stelle an, damit du Variablen ansehen und Schritt für Schritt weitergehen kannst.

**1. Haltepunkt setzen**

Klicke links neben die Zeilennummer oder drücke `F9`. Das Programm stoppt dort.

**2. Mit Debugger starten**

Starte mit `F5`. Am Haltepunkt siehst du die aktuellen Werte aller Variablen.

**3. Schritt für Schritt gehen**

Mit `F10` führst du die Zeile aus, mit `F11` springst du in eine aufgerufene Methode hinein. `F5` läuft bis zum nächsten Haltepunkt.

> [!NOTE]
>
> Die Tastenkürzel gelten für Visual Studio. In Visual Studio Code sind sie sehr ähnlich: `F5` startet, `F9` setzt einen Haltepunkt, `F10` und `F11` gehen Schritt für Schritt.

***

## Dateien lesen und schreiben

Die Klasse `File` im Namespace `System.IO` deckt die häufigsten Fälle mit einem Aufruf ab.

| Methode | Wirkung |
| :- | :- |
| `File.WriteAllText(pfad, text)` | Schreibt Text und **überschreibt** die Datei |
| `File.AppendAllText(pfad, text)` | Hängt Text hinten an |
| `File.ReadAllText(pfad)` | Liest die ganze Datei als einen String |
| `File.WriteAllLines(pfad, zeilen)` | Schreibt jede Zeile einzeln |
| `File.ReadAllLines(pfad)` | Liest alle Zeilen in ein Array |
| `File.Exists(pfad)` | Prüft, ob die Datei existiert |
| `File.Delete(pfad)` | Löscht die Datei |

```csharp theme={"dark"}
string pfad = "notizen.txt";

File.WriteAllText(pfad, "Erste Zeile\n");
File.AppendAllText(pfad, "Zweite Zeile\n");

if (File.Exists(pfad))
{
    string[] zeilen = File.ReadAllLines(pfad);

    foreach (string zeile in zeilen)
    {
        Console.WriteLine(zeile);
    }
}
```

Ein relativer Pfad wie `notizen.txt` bezieht sich auf das **Arbeitsverzeichnis** des Programms. Bei `dotnet run` ist das der Projektordner.

### Pfade zusammensetzen

Baue Pfade nie von Hand zusammen, sondern mit `Path`:

```csharp theme={"dark"}
string ordner = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MeineApp");
Directory.CreateDirectory(ordner);   // legt den Ordner an, falls er fehlt

string datei = Path.Combine(ordner, "daten.txt");
```

### Dateien sicher öffnen

Streams und andere Ressourcen, die belegt werden, schließt `using` automatisch, selbst bei einem Fehler:

```csharp theme={"dark"}
using (StreamWriter schreiber = new StreamWriter("log.txt", append: true))
{
    schreiber.WriteLine($"{DateTime.Now}: Programm gestartet");
}

using StreamReader leser = new StreamReader("log.txt");   // endet am Blockende
string? zeile;
while ((zeile = leser.ReadLine()) != null)
{
    Console.WriteLine(zeile);
}
```

### Dateizugriff mit Fehlerbehandlung

```csharp theme={"dark"}
try
{
    string inhalt = File.ReadAllText("einstellungen.txt");
    Console.WriteLine(inhalt);
}
catch (FileNotFoundException)
{
    Console.WriteLine("Die Datei gibt es noch nicht.");
}
catch (IOException ex)
{
    Console.WriteLine($"Zugriff nicht möglich: {ex.Message}");
}
```

> [!NOTE]
>
> Für große Dateien liest du besser Zeile für Zeile mit `StreamReader`, statt alles auf einmal in den Speicher zu laden.

***

## Weiter

> [card] [4.2.8 Beispielprojekte](https://homelab.amylify.de/documentation/code/csharp/projekte.md)
>
> Das Gelernte in vier kleinen Programmen anwenden.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.