Wird geladen...

IDisposable und das using-Muster in C#

Lernen Sie IDisposable und das using-Muster in C#, um Ressourcen korrekt freizugeben und Speicherlecks zu vermeiden.

In C# wurde das IDisposable-Interface entwickelt, um Ressourcen außerhalb des Speichers (z. B. Dateien, Netzwerkverbindungen, Datenbanken, GDI-Objekte usw.) korrekt freizugeben. Anstatt darauf zu warten, dass das System diese Ressourcen automatisch bereinigt, erfolgt die deterministische (vorhersehbare) Freigabe mit der Dispose-Methode und der using-Struktur.


Was ist IDisposable?

Das IDisposable-Interface enthält nur eine Methode:


public interface IDisposable
{
    void Dispose();
}

Wenn eine Klasse IDisposable implementiert, sollte sie alle verwalteten (managed) und nicht verwalteten (unmanaged) Ressourcen in der Dispose()-Methode freigeben. Der .NET-Garbage-Collector (GC) kann nicht verwaltete Ressourcen nicht automatisch bereinigen, daher ist der Aufruf von Dispose() entscheidend.


Ein einfaches IDisposable-Beispiel

Im folgenden Beispiel öffnet die Klasse DateiSchreiber einen Dateistream und implementiert IDisposable. Wenn Dispose() aufgerufen wird, wird die Datei automatisch geschlossen.


using System;
using System.IO;

class DateiSchreiber : IDisposable
{
    private readonly StreamWriter _writer;
    private bool _disposed = false;

    public DateiSchreiber(string dateiPfad)
    {
        _writer = new StreamWriter(dateiPfad);
    }

    public void Schreiben(string text)
    {
        if (_disposed)
            throw new ObjectDisposedException(nameof(DateiSchreiber));

        _writer.WriteLine(text);
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _writer.Close();
            _writer.Dispose();
            _disposed = true;
            Console.WriteLine("Datei geschlossen und Ressourcen freigegeben.");
        }
    }
}

class Program
{
    static void Main()
    {
        var schreiber = new DateiSchreiber("test.txt");
        schreiber.Schreiben("Hallo Welt!");
        schreiber.Dispose(); // Ressource freigegeben
    }
}

Ressourcenverwaltung mit using

Das using-Schlüsselwort ruft automatisch die Dispose()-Methode auf und sorgt dafür, dass IDisposable-Objekte korrekt bereinigt werden. Wenn der using-Block endet, wird Dispose() automatisch aufgerufen.


using (var datei = new StreamWriter("log.txt"))
{
    datei.WriteLine("Anwendung gestartet: " + DateTime.Now);
} // Dispose() wird hier automatisch aufgerufen

Dadurch entstehen keine Ressourcenlecks, selbst wenn ein Fehler auftritt. Ein using-Block ist äquivalent zu einem try/finally-Block:


var datei = new StreamWriter("log.txt");
try
{
    datei.WriteLine("Anwendung gestartet.");
}
finally
{
    datei.Dispose();
}

Mehrere using-Anweisungen in einer Zeile

Seit C# 8.0 kann mit der sogenannten „using declaration“ eine vereinfachte Syntax verwendet werden. Dabei sind keine geschweiften Klammern nötig; Dispose() wird automatisch beim Verlassen des Gültigkeitsbereichs aufgerufen.


using var datei = new StreamWriter("output.txt");
datei.WriteLine("Diese Datei wird beim Programmende automatisch geschlossen.");

Das Dispose-Muster (erweiterte Verwendung)

Wenn eine Klasse nicht verwaltete Ressourcen besitzt oder von Unterklassen erweitert wird, die selbst IDisposable-Objekte enthalten, sollte das Dispose-Muster implementiert werden. Dieses Muster verwendet Dispose(bool disposing), um verwaltete und nicht verwaltete Ressourcen getrennt freizugeben.


using System;

class RessourcenManager : IDisposable
{
    private IntPtr _unmanagedResource; // Beispiel: Datei-Handle, Socket, GDI-Objekt
    private bool _disposed = false;

    public RessourcenManager()
    {
        _unmanagedResource = IntPtr.Zero; // Beispielinitialisierung
    }

    // Öffentliche Dispose-Methode
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this); // Finalizer-Unterdrückung
    }

    // Geschützte virtuelle Methode, kann von Unterklassen überschrieben werden
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            // verwaltete Ressourcen freigeben
        }

        // nicht verwaltete Ressourcen freigeben
        if (_unmanagedResource != IntPtr.Zero)
        {
            // Beispiel: CloseHandle(_unmanagedResource);
            _unmanagedResource = IntPtr.Zero;
        }

        _disposed = true;
    }

    // Finalizer (Sicherheitsnetz für nicht verwaltete Ressourcen)
    ~RessourcenManager()
    {
        Dispose(false);
    }
}

Durch GC.SuppressFinalize() werden unnötige Finalizer-Aufrufe verhindert und die sichere Freigabe nicht verwalteter Ressourcen gewährleistet.


Asynchrone Ressourcenverwaltung: IAsyncDisposable

Das in C# 8.0 eingeführte IAsyncDisposable-Interface wird für die asynchrone Freigabe von Ressourcen verwendet. Zum Beispiel kann eine Netzwerkverbindung oder ein Stream asynchron geschlossen werden.


using System;
using System.IO;
using System.Threading.Tasks;

class LogSchreiber : IAsyncDisposable
{
    private readonly StreamWriter _writer = new StreamWriter("async_log.txt");

    public async ValueTask DisposeAsync()
    {
        await _writer.FlushAsync();
        _writer.Dispose();
        Console.WriteLine("Asynchroner Log-Schreiber geschlossen.");
    }

    public async Task SchreibenAsync(string nachricht)
    {
        await _writer.WriteLineAsync(nachricht);
    }
}

class Program
{
    static async Task Main()
    {
        await using var log = new LogSchreiber();
        await log.SchreibenAsync("Programm gestartet.");
    }
}

Häufige Fehler und wichtige Hinweise


Beispiel: Dateiverarbeitungsdienst

Im folgenden Beispiel verwaltet eine Serviceklasse Ressourcen wie FileStream und StreamReader. Dank using werden Dateien automatisch geschlossen, sobald die Verarbeitung abgeschlossen ist.


using System;
using System.IO;

class DateiService
{
    public string Lesen(string dateiPfad)
    {
        using var sr = new StreamReader(dateiPfad);
        return sr.ReadToEnd();
    }

    public void Schreiben(string dateiPfad, string daten)
    {
        using var sw = new StreamWriter(dateiPfad, append: true);
        sw.WriteLine(daten);
    }
}

class Program
{
    static void Main()
    {
        var service = new DateiService();
        service.Schreiben("bericht.txt", "Neuer Eintrag hinzugefügt.");
        Console.WriteLine(service.Lesen("bericht.txt"));
    }
}

TL;DR

  • IDisposable ermöglicht die manuelle Freigabe von Ressourcen über Dispose().
  • Die using-Struktur ruft Dispose() automatisch auf und verhindert Ressourcenlecks.
  • Das Dispose-Muster sorgt für eine sichere Bereinigung verwalteter und nicht verwalteter Ressourcen.
  • IAsyncDisposable schließt Ressourcen asynchron (z. B. Datei, Netzwerk).
  • GC.SuppressFinalize() verbessert die Leistung, indem unnötige Finalizer-Aufrufe vermieden werden.

Ähnliche Artikel