Delen via


Objecten gebruiken die IDisposable implementeren

De garbagecollector (GC) van de Common Language Runtime maakt het geheugen vrij dat wordt gebruikt door beheerde objecten. Normaal gesproken implementeren typen die onbeheerde resources gebruiken de IDisposable of IAsyncDisposable interface om de onbeheerde resources vrij te maken. Wanneer u klaar bent met het gebruik van een object dat wordt geïmplementeerdIDisposable, roept u de of DisposeAsync implementatie van Dispose het object aan om expliciet opschoning uit te voeren. U kunt dit op twee manieren doen:

  • Met de C# using -instructie of -declaratie (Using in Visual Basic).
  • Door een try/finally blok te implementeren en de Dispose of DisposeAsync methode in de finally.

Belangrijk

De GC gooit uw objecten niet weg, omdat het geen kennis heeft van IDisposable.Dispose() of IAsyncDisposable.DisposeAsync(). De GC weet alleen of een object kan worden voltooid (dat wil zeggen, er wordt een Object.Finalize() methode gedefinieerd) en wanneer de finalizer van het object moet worden aangeroepen. Zie Hoe voltooien werkt voor meer informatie. Zie voor meer informatie over het implementeren Dispose en DisposeAsyncbekijken van:

Objecten die moeten worden geïmplementeerd System.IDisposable of System.IAsyncDisposable altijd correct moeten worden verwijderd, ongeacht het bereik van variabelen, tenzij anders expliciet wordt vermeld. Typen die een finalizer definiëren om onbeheerde resources vrij te geven, roepen GC.SuppressFinalize meestal aan vanuit hun Dispose of DisposeAsync implementatie. Aanroepen SuppressFinalize geeft aan dat de GC al is uitgevoerd en dat het object niet mag worden gepromoveerd voor voltooien.

De using-instructie

De using instructie in C# en de Using instructie in Visual Basic vereenvoudigen de code die u moet schrijven om een object op te ruimen. De using instructie verkrijgt een of meer resources, voert de instructies uit die u opgeeft en verwijdert automatisch het object. De using instructie is echter alleen nuttig voor objecten die worden gebruikt binnen het bereik van de methode waarin ze worden samengesteld.

In het volgende voorbeeld wordt de using instructie gebruikt om een System.IO.StreamReader object te maken en vrij te geven.

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

Een using declaratie is een alternatieve syntaxis die beschikbaar is wanneer de accolades worden verwijderd en het bereik impliciet is.

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.
            //
        }
    }
}

Hoewel de StreamReader klasse de IDisposable interface implementeert, wat aangeeft dat er een onbeheerde resource wordt gebruikt, wordt de StreamReader.Dispose methode niet expliciet aangeroepen in het voorbeeld. Wanneer de C# of Visual Basic-compiler de using instructie tegenkomt, wordt er een tussenliggende taal (IL) verzonden die gelijk is aan de volgende code die expliciet een try/finally blok bevat.

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

Met de C# using -instructie kunt u ook meerdere resources in één instructie verkrijgen, wat intern gelijk is aan geneste using instructies. In het volgende voorbeeld worden twee StreamReader objecten geïnstitueert om de inhoud van twee verschillende bestanden te lezen.

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.
            //
        }
    }
}

Probeer/ten slotte blokkeren

In plaats van een try/finally blok in een using instructie te verpakken, kunt u ervoor kiezen om het try/finally blok rechtstreeks te implementeren. Het kan uw persoonlijke coderingsstijl zijn of u wilt dit om een van de volgende redenen doen:

  • Als u een catch blok wilt opnemen voor het verwerken van uitzonderingen die in het try blok zijn opgetreden. Anders worden eventuele uitzonderingen die in de using instructie worden gegenereerd, niet verwerkt.
  • Als u een object wilt instantiëren dat implementeert IDisposable waarvan het bereik niet lokaal is voor het blok waarin het wordt gedeclareerd.

Het volgende voorbeeld is vergelijkbaar met het vorige voorbeeld, behalve dat er een blok wordt gebruikt om een try/catch/finallyStreamReader object te instantiëren, gebruiken en verwijderen, en om eventuele uitzonderingen te verwerken die zijn gegenereerd door de constructor en ReadToEnd de StreamReader bijbehorende methode. De code in het finally blok controleert of het object dat wordt geïmplementeerd IDisposable niet null voordat de Dispose methode wordt aangeroepen. Als u dit niet doet, kan dit leiden tot een NullReferenceException uitzondering tijdens runtime.

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

U kunt dit basispatroon volgen als u ervoor kiest om een try/finally blok te implementeren of te implementeren, omdat uw programmeertaal geen ondersteuning biedt voor een using instructie, maar wel directe aanroepen naar de Dispose methode toestaat.

Leden van IDisposable-exemplaren

Als een klasse eigenaar is van een exemplaarveld of eigenschap en het bijbehorende type implementeert IDisposable, moet de klasse ook implementeren IDisposable. Zie Een trapsgewijs verwijderen implementeren voor meer informatie.

Zie ook