Dela via


Använda objekt som implementerar IDisposable

Common Language Runtimes skräpinsamlare (GC) återtar det minne som används av hanterade objekt. Vanligtvis implementerar IDisposable typer som använder ohanterade resurser eller IAsyncDisposable -gränssnittet så att ohanterade resurser kan frigöras. När du är klar med ett objekt som implementerar anropar IDisposabledu objektets Dispose eller DisposeAsync implementeringen för att uttryckligen utföra rensningen. Det kan du göra på ett av följande två vis:

  • Med C#- using instruktionen eller deklarationen (Using i Visual Basic).
  • Genom att implementera ett try/finally block och anropa Dispose metoden eller DisposeAsync i finally.

Viktigt!

GC tar inte bort dina objekt eftersom den inte har någon kunskap om IDisposable.Dispose() eller IAsyncDisposable.DisposeAsync(). GC vet bara om ett objekt kan slutföras (dvs. definierar en Object.Finalize() metod) och när objektets finaliserare måste anropas. Mer information finns i Så här fungerar slutförande. Mer information om hur du implementerar Dispose och DisposeAsyncfinns i:

Objekt som implementerar System.IDisposable eller System.IAsyncDisposable alltid ska tas bort korrekt, oavsett variabelomfång, om inget annat uttryckligen anges. Typer som definierar en finalizer för att frigöra ohanterade resurser anropar GC.SuppressFinalize vanligtvis från antingen deras Dispose eller DisposeAsync implementeringen. Anrop SuppressFinalize anger för GC att slutföraren redan har körts och att objektet inte bör höjas upp för slutförande.

Instruktionen using

Instruktionen using i C# och -instruktionen Using i Visual Basic förenklar koden som du måste skriva för att rensa ett objekt. -instruktionen using hämtar en eller flera resurser, kör de instruktioner som du anger och tar bort objektet automatiskt. Instruktionen using är dock endast användbar för objekt som används inom omfånget för den metod där de konstrueras.

I följande exempel används -instruktionen using för att skapa och släppa ett System.IO.StreamReader objekt.

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

En using deklaration är en alternativ syntax som är tillgänglig där klammerparenteserna tas bort och omfånget är implicit.

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

StreamReader Även om klassen implementerar IDisposable gränssnittet, vilket indikerar att den använder en ohanterad resurs, anropar exemplet inte uttryckligen StreamReader.Dispose metoden. När C# eller Visual Basic-kompilatorn stöter på -instruktionen using genererar den mellanliggande språk (IL) som motsvarar följande kod som uttryckligen innehåller ett try/finally block.

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

Med C#- using instruktionen kan du också hämta flera resurser i en enda instruktion, vilket är internt likvärdigt med kapslade using instruktioner. I följande exempel instansieras två StreamReader objekt för att läsa innehållet i två olika filer.

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

Försök/slutligen blockera

I stället för att omsluta ett try/finally block i en using -instruktion kan du välja att implementera try/finally blocket direkt. Det kan vara din personliga kodningsstil, eller så kanske du vill göra detta av någon av följande orsaker:

  • Inkludera ett catch block för att hantera undantag som genereras i try blocket. Annars hanteras alla undantag som genereras i -instruktionen using .
  • Om du vill instansiera ett objekt som implementerar IDisposable vars omfång inte är lokalt för det block där det deklareras.

Följande exempel liknar föregående exempel, förutom att det använder ett try/catch/finally block för att instansiera, använda och ta bort ett StreamReader objekt och hantera eventuella undantag som genereras av StreamReader konstruktorn och dess ReadToEnd metod. Koden i finally blocket kontrollerar att objektet som implementerar IDisposable inte null är innan det anropar Dispose metoden. Om du inte gör det kan det leda till ett NullReferenceException undantag vid körning.

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

Du kan följa det här grundläggande mönstret om du väljer att implementera eller måste implementera ett try/finally block, eftersom programmeringsspråket inte stöder en using instruktion, men tillåter direkta anrop till Dispose metoden.

IDisposable-instansmedlemmar

Om en klass äger ett instansfält eller en instansegenskap och dess typ implementerar IDisposablebör klassen även implementera IDisposable. Mer information finns i Implementera en kaskadborttagning.

Se även