Использование объектов, реализующих IDisposableUsing objects that implement IDisposable

Сборщик мусора среды CLR освобождает память, используемую управляемыми объектами. Но типы, которые используют неуправляемые ресурсы, реализуют интерфейс IDisposable, который позволяет освободить ресурсы, которые требуются этим неуправляемым ресурсам.The common language runtime's garbage collector reclaims the memory used by managed objects, but types that use unmanaged resources implement the IDisposable interface to allow the resources needed by these unmanaged resources to be reclaimed. По окончании использования объекта, который реализует интерфейс IDisposable, необходимо вызвать реализацию объекта IDisposable.Dispose.When you finish using an object that implements IDisposable, you should call the object's IDisposable.Dispose implementation. Это можно сделать одним из двух способов.You can do this in one of two ways:

  • С помощью оператора using (C#) или Using (Visual Basic).With the C# using statement (Using in Visual Basic).
  • Путем реализации блока try/finally и вызова IDisposable.Dispose в finally.By implementing a try/finally block, and calling the IDisposable.Dispose in the finally.

Оператор usingThe using statement

Оператор using (C#) и оператор Using (Visual Basic) упрощают написание кода для очистки объекта.The using statement in C# and the Using statement in Visual Basic simplify the code that you must write to cleanup an object. Оператор using получает один или больше ресурсов, выполняет заданные операторы, после чего автоматически освобождает объект.The using statement obtains one or more resources, executes the statements that you specify, and automatically disposes of the object. Однако оператор using полезен только для объектов в области действия метода, в котором они созданы.However, the using statement is useful only for objects that are used within the scope of the method in which they are constructed.

В следующем примере для создания и освобождения объекта using используется оператор System.IO.StreamReader.The following example uses the using statement to create and release a System.IO.StreamReader object.

using System;
using System.IO;

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

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

Хотя класс StreamReader реализует интерфейс IDisposable, который указывает, что используется неуправляемый ресурс, пример явно не вызывает метод StreamReader.Dispose.Although the StreamReader class implements the IDisposable interface, which indicates that it uses an unmanaged resource, the example doesn't explicitly call the StreamReader.Dispose method. Когда компилятор C# или Visual Basic встречает оператор using, он создает промежуточный язык, эквивалентный следующему коду, который явно содержит блок try/finally.When the C# or Visual Basic compiler encounters the using statement, it emits intermediate language (IL) that is equivalent to the following code that explicitly contains a try/finally block.

using System;
using System.IO;

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

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

Оператор using в C# позволяет присоединять несколько ресурсов в одном операторе, что эквивалентно использованию вложенных инструкций using.The C# using statement also allows you to acquire multiple resources in a single statement, which is internally equivalent to nested using statements. В следующем примере создается два объекта StreamReader для чтения содержимого двух разных файлов.The following example instantiates two StreamReader objects to read the contents of two different files.

using System.IO;

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

        using StreamReader version1 = new StreamReader("file1.txt"),
                           version2 = new StreamReader("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/finallyTry/finally block

Вместо размещения блока try/finally в операторе using можно реализовать блок try/finally напрямую.Instead of wrapping a try/finally block in a using statement, you may choose to implement the try/finally block directly. Это может отражать ваш стиль программирования или же осуществляться по одной из следующих причин:It may be your personal coding style, or you might want to do this for one of the following reasons:

  • Чтобы включить блок catch для обработки исключений, вызванных в блоке try.To include a catch block to handle exceptions thrown in the try block. В противном случае любые исключения, вызванные в операторе using, не обрабатываются.Otherwise, any exceptions thrown within the using statement are unhandled.

  • Чтобы создать экземпляр объекта, реализующего интерфейс IDisposable, область действия которого не является локальной для блока, в котором он объявлен.To instantiate an object that implements IDisposable whose scope is not local to the block within which it is declared.

Следующий пример похож на предыдущий с тем отличием, что в нем используется блок try/catch/finallyдля создания, использования и удаления экземпляра объекта StreamReader, а также для обработки исключений, создаваемых конструктором StreamReader и его методом ReadToEnd.The following example is similar to the previous example, except that it uses a try/catch/finally block to instantiate, use, and dispose of a StreamReader object, and to handle any exceptions thrown by the StreamReader constructor and its ReadToEnd method. Перед вызовом метода Dispose код в блоке finally проверяет, что объект, реализующий IDisposable, не является null.The code in the finally block checks that the object that implements IDisposable isn't null before it calls the Dispose method. Если этого сделать не удастся, это может привести к исключению NullReferenceException во время выполнения.Failure to do this can result in a NullReferenceException exception at run time.

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

class Example
{
    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 Example
    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

Вы можете применить этот базовый шаблон, если есть желание или необходимость реализовать блок try/finally на языке программирования, который не поддерживает оператор using, но допускает прямые вызовы метода Dispose.You can follow this basic pattern if you choose to implement or must implement a try/finally block, because your programming language doesn't support a using statement but does allow direct calls to the Dispose method.

Члены экземпляра IDisposableIDisposable instance members

Если класс содержит реализацию IDisposable в качестве члена экземпляра (поля или свойства), класс также должен реализовывать IDisposable.If a class holds an IDisposable implementation as an instance member, either a field or a property, the class should also implement IDisposable. См. сведения о реализации каскадного удаления.For more information, see implement a cascade dispose.

См. такжеSee also