Použití objektů, které implementují IDisposable

Uvolňování paměti (GC) modulu CLR (Common Language Runtime) uvolňuje paměť využívaná spravovanými objekty. Obvykle typy, které používají nespravované prostředky, implementují IDisposable rozhraní nebo, IAsyncDisposable aby bylo možné nespravované prostředky uvolnit. Po dokončení použití objektu, který implementuje IDisposable , zavolejte Dispose nebo DisposeAsync implementujte objekt a explicitně proveďte vyčištění. Toto lze provést jedním ze dvou způsobů:

  • Pomocí using příkazu nebo deklarace jazyka C# ( Using v Visual Basic).
  • Implementací try/finally bloku a voláním Dispose DisposeAsync metody nebo v finally .

Důležité

GC neuvolní vaše objekty, protože nemá žádné znalosti o IDisposable.Dispose() nebo IAsyncDisposable.DisposeAsync() . GC pouze ví, zda je objekt možné Finalize (to znamená, že definuje Object.Finalize() metodu) a v případě, že je nutné volat finalizační metodu objektu. Další informace najdete v tématu Jak funguje finalizace. Další podrobnosti o implementaci Dispose a DisposeAsync najdete v těchto tématech:

Objekty, které implementují System.IDisposable nebo System.IAsyncDisposable by měly být vždy trvale odstraněny, bez ohledu na rozsah proměnné, pokud není výslovně uvedeno jinak. Typy, které definují finalizační metodu pro uvolnění nespravovaných prostředků obvykle volají GC.SuppressFinalize z jejich Dispose nebo DisposeAsync implementace. Volání SuppressFinalize označuje GC, který již byl spuštěn finalizační metoda a objekt by neměl být povýšen na finalizaci.

Pomocí příkazu using

using Příkaz v jazyce C# a Using příkaz v Visual Basic zjednoduší kód, který je nutné zapsat pro vyčištění objektu. usingPříkaz získá jeden nebo více prostředků, spustí příkazy, které zadáte, a automaticky odstraní objekt. usingPříkaz je však vhodný pouze pro objekty, které jsou používány v rámci rozsahu metody, ve které jsou vytvořeny.

Následující příklad používá using příkaz k vytvoření a uvolnění System.IO.StreamReader objektu.

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

V jazyce C# 8 je using deklarace alternativní syntaxí, která je k dispozici, když jsou složené závorky odebrány a obor je implicitní.

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

I když třída implementuje rozhraní, které označuje, že používá nespravovaný prostředek, příklad explicitně StreamReader nevolá IDisposable StreamReader.Dispose metodu . Když kompilátor jazyka C# nebo Visual Basic narazí na příkaz , vysílá zprostředkující jazyk (IL), který je ekvivalentní následujícímu kódu, který using explicitně obsahuje try/finally blok.

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

Příkaz jazyka C# také umožňuje získat více prostředků v jednom příkazu, který je using interně ekvivalentní vnořeným using příkazům. Následující příklad vytvoří instanci dvou StreamReader objektů pro čtení obsahu dvou různých souborů.

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

Testovací blok / bezpodmínečný blok

Místo try/finally zabalení bloku do using příkazu se můžete rozhodnout implementovat blok try/finally přímo. Může to být váš styl osobního kódování nebo to můžete chtít udělat z jednoho z následujících důvodů:

  • Chcete-li catch zahrnout blok pro zpracování výjimek vyvolaných v try bloku. V opačném případě jsou všechny výjimky vyvolané v using rámci příkazu neošetřené.
  • Vytvoření instance objektu, který implementuje, IDisposable jehož obor není místní pro blok, ve kterém je deklarován.

Následující příklad je podobný předchozímu příkladu s tím rozdílem, že používá blok k vytvoření instance, použití a uvolnění objektu a ke zpracování všech výjimek vyvolaných konstruktorem try/catch/finally a StreamReader jeho StreamReader ReadToEnd metodou. Kód v bloku kontroluje, zda objekt, který implementuje , není před finally IDisposable null voláním Dispose metody . Pokud to není možné, může dojít NullReferenceException k výjimce za běhu.

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

Pokud se rozhodnete implementovat nebo musíte implementovat blok, můžete postupovat podle tohoto základního vzoru, protože programovací jazyk nepodporuje příkaz, ale umožňuje přímá volání try/finally using metody Dispose .

Členové instance IDisposable

Pokud třída obsahuje IDisposable implementaci jako člen instance, měla by být třída také implementovaná buď pole, nebo vlastnost IDisposable . Další informace najdete v tématu implementace kaskádové likvidace.

Viz také