Sdílet prostřednictvím


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

Sběrač paměti (GC) modulu CLR (Common Language Runtime) obnovuje paměť používanou spravovanými objekty. Typy, které používají nespravované prostředky, obvykle implementují IDisposable nebo IAsyncDisposable rozhraní, aby bylo možné nespravované prostředky uvolnit. Po dokončení použití objektu, který implementuje IDisposable, zavoláte implementaci objektu Dispose nebo DisposeAsync pro explicitní vyčištění. Můžete to udělat jedním ze dvou způsobů:

  • S příkazem nebo deklarací jazyka C# using (Using v jazyce Visual Basic)
  • Implementací bloku try/finally a voláním metody Dispose nebo DisposeAsync v rámci finally.

Důležité

GC nelikviduje vaše objekty, protože nemá znalosti o IDisposable.Dispose() nebo IAsyncDisposable.DisposeAsync(). GC pouze ví, zda je objekt finalizovatelný (tj. definuje metodu Object.Finalize() ) a kdy je třeba volat finalizátor objektu. Další informace naleznete v tématu Jak finalizace funguje. Pro další podrobnosti o implementaci Dispose a DisposeAsync najdete v tématech:

Objekty, které implementují System.IDisposable, nebo System.IAsyncDisposable, by měly být vždy řádně uvolněny bez ohledu na rozsah proměnných, pokud není výslovně uvedeno jinak. Typy, které definují finalizátor pro uvolnění nespravovaných prostředků, obvykle volají GC.SuppressFinalize ze své Dispose nebo DisposeAsync implementace. Volání SuppressFinalize znamená pro GC, že finalizátor již byl spuštěn a objekt by neměl být určen k finalizaci.

Příkaz using

Příkaz using v jazyce C# a příkazUsing v jazyce Visual Basic zjednodušují kód, který je nutné napsat pro vyčištění objektu. Příkaz using získá jeden nebo více prostředků, spustí zadané příkazy a automaticky odstraní objekt. Příkaz using je však užitečný pouze pro objekty, které se používají v rámci oboru metody, ve které jsou vytvořené.

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

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

Deklaraceusing je alternativní syntaxí, která je k dispozici ve chvíli, kdy jsou složené závorky odstraněny a rozsah se stává implicitním.

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

Ačkoli třída StreamReader implementuje rozhraní IDisposable, což označuje, že používá nespravovaný prostředek, příklad explicitně nevolá metodu StreamReader.Dispose. Když kompilátor jazyka C# nebo Visual Basic narazí na using příkaz, vygeneruje zprostředkující jazyk (IL), který je ekvivalentní následujícímu kódu, který 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# using také umožňuje získat více prostředků v jednom příkazu, což je 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.
            //
        }
    }
}

Try/finally blok

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

  • Pro zahrnutí catch bloku pro zpracování výjimek vyvolaných v try bloku. V opačném případě jsou všechny výjimky vyvolané v příkazu using neošetřeny.
  • 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 try/catch/finally , že používá blok k vytvoření instance, použití a odstranění objektu StreamReader a zpracování všech výjimek vyvolaných konstruktorem StreamReader a jeho ReadToEnd metodou. Kód v finally bloku zkontroluje, že objekt, který implementuje IDisposable , není null před voláním Dispose metody. Pokud to neuděláte, může dojít k výjimce NullReferenceException při běhu programu.

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

Tento základní vzor můžete použít, pokud se rozhodnete implementovat nebo musíte implementovat try/finally blok, protože váš programovací jazyk nepodporuje using příkaz, ale umožňuje přímé volání metody Dispose .

Členové instance IDisposable

Pokud třída vlastní pole instance nebo vlastnost a jeho typ implementuje IDisposable, třída by měla také implementovat IDisposable. Další informace naleznete v tématu Implementace kaskádové odstranění.

Viz také