Реализация метода Dispose

Шаблон удаления объекта упорядочивает жизненный цикл этого объекта. Шаблон удаления используется только для объектов, осуществляющих доступ к неуправляемым ресурсам. Это вызвано тем, что сборщик мусора очень эффективно удаляет неиспользуемые управляемые объекты.

Метод Disposeдолжен освободить все удерживаемые им ресурсы. Он также должен освободить все ресурсы, которыми владеют его базовые типы, путем вызова метода Dispose его родительского типа. Метод Dispose родительского типа должен освободить все удерживаемые им ресурсы и, в свою очередь, вызвать метод Dispose своего родительского типа, распространяя данный шаблон по иерархии базовых типов. Чтобы обеспечить соответствующую очистку ресурсов, метод Dispose должен быть доступен для многократного вызова без выдачи исключения.

Применение метода Dispose для типов, которые используют только управляемые ресурсы (например массивов) не ведет к повышению производительности, поскольку они автоматически утилизируются сборщиком мусора. Метод Dispose следует использовать для управляемых объектов, которые используют собственные ресурсы, а также в отношении COM-объектов, доступных в платформе .NET Framework. Управляемые объекты, которые используют собственные ресурсы (например класс FileStream), реализуют интерфейс IDisposable.

Важное примечаниеВажно

Программистам, работающим с языком C++, не следует руководствоваться инструкциями, приведенными в этой теме.Вместо этого следует изучить тему Destructors and Finalizers in Visual C++.В версии платформы .NET Framework 2.0 компилятор C++ обеспечивает поддержку реализации детерминированной утилизации ресурсов и не позволяет реализовать метод Dispose непосредственно.

Метод Dispose должен вызвать для удаляемого им объекта метод SuppressFinalize. Если объект уже помещен в очередь завершения, метод SuppressFinalize блокирует вызов его метода Finalize. Следует помнить, что выполнение метода Finalize представляет собой дорогостоящую операцию с точки зрения производительности. Если метод Dispose уже завершил работу по очистке объекта, необязательно вызывать для сборщика мусора метод Finalize объекта.

В приведенном для метода GC.KeepAlive примере показано, как чрезмерно активная сборка мусора может привести к выполнению финализатора до завершения выполнения утилизируемого объекта. Рекомендуется вызывать метод KeepAlive в конце большого метода Dispose.

Альтернатива SafeHandle

Написание кода для метода завершения объекта является сложной задачей, которая может вызвать проблемы при неправильном выполнении. Поэтому вместо реализации шаблона Dispose рекомендуется создавать объекты SafeHandle.

Класс SafeHandle упрощает проблемы времени существования объекта, назначая и освобождая дескрипторы без прерываний. Он содержит критический метод завершения, который обязательно выполняется во время выгрузки домена приложения. Дополнительные сведения о преимуществах использования безопасного дескриптора см. в разделе Безопасные дескрипторы и критическое завершение.

Класс SafeHandle в пространстве имен System.Runtime.InteropServices является абстрактным классом-оболочкой для дескрипторов операционной системы. Наследовать от этого класса сложно. Вместо этого используйте производные классы в пространстве имен Microsoft.Win32.SafeHandles, которые предоставляют безопасные дескрипторы для следующих элементов.

  • Файлы и каналы.

  • Представления памяти.

  • Конструкции шифрования.

  • Разделы реестра.

  • Дескрипторы ожидания.

Пример

В следующем примере показан рекомендуемый шаблон реализации метода Dispose для классов, которые инкапсулируют неуправляемые ресурсы.

Ресурсные классы обычно являются производными от сложных внутренних классов или интерфейсов API и должны быть соответственно настроены. Этот шаблон кода можно использовать как отправную точку для создания ресурсного класса и проведения необходимой настройки в соответствии с инкапсулируемыми ресурсами.

Imports System
Imports System.IO
Class Program

    Public Shared Sub Main()
        Try
            ' Initialize a Stream resource to pass 
            ' to the DisposableResource class.
           Console.Write("Enter filename and its path: ")
            Dim fileSpec As String = Console.ReadLine
            Dim fs As FileStream = File.OpenRead(fileSpec)
            Dim TestObj As DisposableResource = New DisposableResource(fs)

            ' Use the resource.
            TestObj.DoSomethingWithResource()

            ' Dispose theresource.
            TestObj.Dispose()

        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try
    End Sub
End Class

' This class shows how to use a disposable resource.
' The resource is first initialized and passed to
' the constructor, but it could also be
' initialized in the constructor.
' The lifetime of the resource does not 
' exceed the lifetime of this instance.
' This type does not need a finalizer because it does not
' directly create a native resource like a file handle
' or memory in the unmanaged heap.
Public Class DisposableResource
    Implements IDisposable

    Private _resource As Stream

    Private _disposed As Boolean

    ' The stream passed to the constructor
    ' must be readable and not null.
    Public Sub New(ByVal stream As Stream)
        MyBase.New()
        If (stream Is Nothing) Then
            Throw New ArgumentNullException("Stream is null.")
        End If
        If Not stream.CanRead Then
            Throw New ArgumentException("Stream must be readable.")
        End If
        _resource = stream
        Dim objTypeName As String = _resource.GetType.ToString
        _disposed = False
    End Sub

    ' Demonstrates using the resource.
    ' It must not be already disposed.
    Public Sub DoSomethingWithResource()
        If _disposed Then
            Throw New ObjectDisposedException("Resource was disposed.")
        End If

        ' Show the number of bytes.
        Dim numBytes As Integer = CType(_resource.Length, Integer)
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString)
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)

        ' Use SupressFinalize in case a subclass
        ' of this type implements a finalizer.
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then

            ' If you need thread safety, use a lock around these 
            ' operations, as well as in your methods that use the resource.
            If disposing Then
                If (Not (_resource) Is Nothing) Then
                    _resource.Dispose()
                End If
                Console.WriteLine("Object disposed.")
            End If

            ' Indicates that the instance has been disposed.
            _resource = Nothing
            _disposed = True
        End If
    End Sub
End Class
using System;
using System.IO;

class Program
{

    static void Main()
    {
        try
        {
            // Initialize a Stream resource to pass 
            // to the DisposableResource class.
            Console.Write("Enter filename and its path: ");
            string fileSpec = Console.ReadLine();
            FileStream fs = File.OpenRead(fileSpec);
            DisposableResource TestObj = new DisposableResource(fs);

            // Use the resource.
            TestObj.DoSomethingWithResource();

            // Dispose the resource.
            TestObj.Dispose();

        }
        catch (FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

См. также

Ссылки

SuppressFinalize

Destructors and Finalizers in Visual C++

Реализация методов Finalize и Dispose для очистки неуправляемых ресурсов

Основные понятия

Переопределение метода Finalize