Freigeben über


Verwenden von Objekten, die IDisposable implementieren

Der Garbage Collector (GC) der Common Language Runtime gibt den von verwalteten Objekten verwendeten Speicher frei. In der Regel implementieren Typen, die nicht verwaltete Ressourcen verwenden, die oder IAsyncDisposable die IDisposable Schnittstelle, damit die nicht verwalteten Ressourcen erneut beansprucht werden können. Wenn Sie mit der Verwendung eines Objekts fertig sind, das implementiert IDisposablewird, rufen Sie die Objekt Dispose - oder DisposeAsync Implementierung auf, um explizit eine Bereinigung durchzuführen. Sie können dies auf eine von zwei Arten tun:

  • Mit der C# using -Anweisung oder -Deklaration (Using in Visual Basic).
  • Durch Implementieren eines try/finally Blocks und Aufrufen der Dispose Oder-Methode DisposeAsync in der finally.

Von Bedeutung

Die GC entsorgt Ihre Objekte nicht , da sie keine Kenntnisse IDisposable.Dispose() hat oder IAsyncDisposable.DisposeAsync(). Die GC weiß nur, ob ein Objekt abgeschlossen werden kann (d. h. es definiert eine Object.Finalize() Methode), und wenn der Finalizer des Objekts aufgerufen werden muss. Weitere Informationen finden Sie unter "Funktionsweise der Finalisierung". Weitere Details zur Implementierung Dispose und DisposeAsyncfinden Sie unter:

Objekte, die implementieren System.IDisposable oder System.IAsyncDisposable immer ordnungsgemäß verworfen werden sollten, unabhängig von variablen Bereichsdefinitionen, sofern nicht anders angegeben. Typen, die einen Finalizer definieren, um nicht verwaltete Ressourcen freizugeben, rufen GC.SuppressFinalize in der Regel von deren Dispose Oder DisposeAsync implementierung ab. Das Aufrufen SuppressFinalize gibt an, dass der Finalizer bereits ausgeführt wurde, und das Objekt sollte nicht für die Finalisierung heraufgestuft werden.

Die using-Anweisung

Die using Anweisung in C# und die Using Anweisung in Visual Basic vereinfachen den Code, den Sie schreiben müssen, um ein Objekt zu bereinigen. Die using Anweisung ruft eine oder mehrere Ressourcen ab, führt die von Ihnen angegebenen Anweisungen aus und entfernt das Objekt automatisch. Die using Anweisung ist jedoch nur für Objekte nützlich, die im Rahmen der Methode verwendet werden, in der sie erstellt werden.

Im folgenden Beispiel wird die using Anweisung verwendet, um ein System.IO.StreamReader Objekt zu erstellen und freizugeben.

using System.IO;

class UsingStatement
{
    static void Main()
    {
        var buffer = new char[50];
        using (StreamReader streamReader = new("file1.txt"))
        {
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
    }
}
Imports System.IO

Module UsingStatement
    Public Sub Main()
        Dim buffer(49) As Char
        Using streamReader As New StreamReader("File1.txt")
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        End Using
    End Sub
End Module

Eine using Deklaration ist eine alternative Syntax, die verfügbar ist, wenn die geschweiften Klammern entfernt werden, und die Bereichsdefinition ist implizit.

using System.IO;

class UsingDeclaration
{
    static void Main()
    {
        var buffer = new char[50];
        using StreamReader streamReader = new("file1.txt");

        int charsRead = 0;
        while (streamReader.Peek() != -1)
        {
            charsRead = streamReader.Read(buffer, 0, buffer.Length);
            //
            // Process characters read.
            //
        }
    }
}

Obwohl die StreamReader Klasse die IDisposable Schnittstelle implementiert, die angibt, dass sie eine nicht verwaltete Ressource verwendet, ruft das Beispiel die StreamReader.Dispose Methode nicht explizit auf. Wenn der C#- oder Visual Basic-Compiler auf die using Anweisung trifft, gibt er Zwischensprache (IL) aus, die dem folgenden Code entspricht, der explizit einen try/finally Block enthält.

using System.IO;

class TryFinallyGenerated
{
    static void Main()
    {
        var buffer = new char[50];
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
        finally
        {
            // If non-null, call the object's Dispose method.
            streamReader?.Dispose();
        }
    }
}
Imports System.IO

Module TryFinallyGenerated
    Public Sub Main()
        Dim buffer(49) As Char
        Dim streamReader As New StreamReader("File1.txt")
        Try
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        Finally
            If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
        End Try
    End Sub
End Module

Mit der C# using -Anweisung können Sie auch mehrere Ressourcen in einer einzelnen Anweisung abrufen, die intern geschachtelten using Anweisungen entspricht. Im folgenden Beispiel werden zwei StreamReader Objekte instanziiert, um den Inhalt von zwei verschiedenen Dateien zu lesen.

using System.IO;

class SingleStatementMultiple
{
    static void Main()
    {
        var buffer1 = new char[50];
        var buffer2 = new char[50];

        using StreamReader version1 = new("file1.txt"),
                           version2 = new("file2.txt");

        int charsRead1, charsRead2 = 0;
        while (version1.Peek() != -1 && version2.Peek() != -1)
        {
            charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
            charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
            //
            // Process characters read.
            //
        }
    }
}

Try/finally block

Anstatt einen try/finally Block in eine using Anweisung umzuschließen, können Sie den try/finally Block direkt implementieren. Möglicherweise handelt es sich um Ihren persönlichen Codierungsstil, oder Sie möchten dies aus einem der folgenden Gründe tun:

  • So fügen Sie einen catch Block zum Behandeln von Ausnahmen in den try Block ein. Andernfalls werden alle ausnahmen, die innerhalb der using Anweisung ausgelöst werden, nicht behandelt.
  • Um ein Objekt zu instanziieren, das implementiert IDisposable , dessen Bereich nicht lokal für den Block ist, in dem es deklariert wird.

Das folgende Beispiel ähnelt dem vorherigen Beispiel, mit der Ausnahme, dass ein try/catch/finally Block zum Instanziieren, Verwenden und Löschen eines StreamReader Objekts verwendet wird, und zum Behandeln von Ausnahmen, die vom StreamReader Konstruktor und dessen ReadToEnd Methode ausgelöst werden. Der Code im finally Block überprüft, ob das implementierte IDisposable Objekt nicht null vor dem Aufrufen der Dispose Methode erfolgt. Wenn dies nicht möglich ist, kann dies zur Laufzeit zu einer NullReferenceException Ausnahme führen.

using System;
using System.Globalization;
using System.IO;

class TryExplicitCatchFinally
{
    static void Main()
    {
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            string contents = streamReader.ReadToEnd();
            var info = new StringInfo(contents);
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file cannot be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("An I/O error has occurred.");
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("There is insufficient memory to read the file.");
        }
        finally
        {
            streamReader?.Dispose();
        }
    }
}
Imports System.Globalization
Imports System.IO

Module TryExplicitCatchFinally
    Sub Main()
        Dim streamReader As StreamReader = Nothing
        Try
            streamReader = New StreamReader("file1.txt")
            Dim contents As String = streamReader.ReadToEnd()
            Dim info As StringInfo = New StringInfo(contents)
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
        Catch e As FileNotFoundException
            Console.WriteLine("The file cannot be found.")
        Catch e As IOException
            Console.WriteLine("An I/O error has occurred.")
        Catch e As OutOfMemoryException
            Console.WriteLine("There is insufficient memory to read the file.")
        Finally
            If streamReader IsNot Nothing Then streamReader.Dispose()
        End Try
    End Sub
End Module

Sie können diesem grundlegenden Muster folgen, wenn Sie einen try/finally Block implementieren oder implementieren möchten, da Ihre Programmiersprache keine using Anweisung unterstützt, aber direkte Aufrufe an die Dispose Methode zulässt.

IDisposable-Instanzmember

Wenn eine Klasse ein Instanzfeld oder eine Instanzeigenschaft besitzt und deren Typ implementiert wird IDisposable, sollte die Klasse auch implementiert werden IDisposable. Weitere Informationen finden Sie unter Implementieren einer Löschweiterung.

Siehe auch