Описание тега idisposable

IDisposable - это интерфейс в библиотеке базовых классов Microsoft .NET Framework (BCL). Он предназначен для предоставления универсального детерминированного метода освобождения неуправляемых ресурсов в коде приложения.NET.

Цель

IDisposable- это интерфейс в библиотеке базовых классов Microsoft .NET Framework (BCL). Он предназначен для предоставления универсального детерминированного метода освобождения неуправляемых ресурсов в коде приложения.NET.

Неуправляемые ресурсы

Удалось ресурс любой объект в памяти, что, в целом, можно контролировать и выпущен.NET сборщик мусора, когда они больше не нужны. Подавляющее большинство типов, доступных в библиотеках.NET (а также определяемых пользователем типов), представляют собой управляемые ресурсы. Поскольку это управляемые ресурсы, нет необходимости освобождать память, которая требуется этим объектам; когда они больше не используются, сборщик мусора.NET будет периодически "собирать" эти объекты и освобождать эту память для других целей.

Неуправляемый ресурс - это любой ресурс (обычно существующий вне среды выполнения приложения, такой как дескриптор файла или соединение с базой данных), который не отслеживается и не может быть выпущен сборщиком мусора среды выполнения.NET. Типичными примерами неуправляемых ресурсов являются сетевые подключения, дескрипторы файлов и графические дескрипторы Windows GDI. Все эти ресурсы, хотя и представлены в.NET-коде традиционными объектами, находятся вне досягаемости сборщика мусора.NET, поскольку процедура получения и освобождения таких ресурсов зависит от типа ресурса, и все они требуют вызовов в неуправляемый код.

IDisposable

IDisposable - это интерфейс внутри BCL, который определяет одну без параметров и невозвратную функцию, называемую Dispose. Тип, реализующий этот интерфейс, указывает разработчику, что на некотором уровне инкапсуляции этот тип использует какой-то неуправляемый ресурс. В этом случае разработчику необходимо позвонитьDisposeна экземпляре, когда он больше не нужен. Реализация базового типаDispose затем должен выполнить все действия, необходимые для освобождения ресурса, включая вызов неуправляемого кода.

Случаи применения

Есть два * (по дизайну) случая, когда тип должен реализовывать IDisposable:

  1. Тип взаимодействует напрямую с неуправляемым ресурсом (он вызывает неуправляемую или внешнюю функцию для получения ресурса и другую неуправляемую или внешнюю функцию для его освобождения).
  2. Тип использует неуправляемый ресурс косвенно через другие типы, реализующиеIDisposable. Другими словами, тип использует неуправляемый ресурс, который не управляется другим экземпляром и существует вне времени существования одного вызова функции.

В первом случае использование очевидно: тип использует ресурс напрямую, поэтому он должен получать и освобождать ресурс напрямую. Во втором случае тип использует другиеIDisposable типы и должны знать, когда можно позвонить Dispose на этих ресурсах.

* Тип может также реализовывать IDisposable чтобы воспользоваться using языковые конструкции в VB.NET и C#, но этот выбор обычно делается по эстетическим или идиоматическим причинам, а не из технической применимости, поэтому эти случаи выходят за рамки данной статьи.

Реализация

В зависимости от причины, по которой тип реализует IDisposableинтерфейс, реализация может незначительно отличаться. Как правило, существует две формы реализацииIDisposable:

  1. Простая, строгая реализация интерфейса (единый Dispose функция)
  2. Реализация, совместимая с финализатором (две версии Dispose)

просто

Если тип относится ко второму варианту использования, описанному выше (косвенное использование неуправляемого ресурса исключительно путем инкапсуляции), то следует использовать первую реализацию. Пример показан ниже:

public class WidgetFile : IDisposable
{
    private FileStream fileStream;

    public WidgetFile(string fileName)
    {
        fileStream = new FileStream(fileName, FileMode.Open);
    }

    public void Dispose()
    {
        fileStream.Dispose();
    }
}

В этом случае WidgetFile инкапсулируетIDisposable тип, System.IO.FileStream. Поскольку тип (и его родительские типы) только косвенно взаимодействуют с неуправляемым ресурсом, простая реализация является адекватной и предпочтительной.

На основе финализатора

Если, однако, тип (или один из его родительских типов) напрямую взаимодействует с неуправляемым ресурсом, то требуется более защитный подход на основе финализатора:

[DllImport("Widget.dll")]
private static extern IntPtr GetGadgetHandle(string fileName);
[DllImport("Widget.dll")]
private static extern void ReleaseGadgetHandle(IntPtr handle);

public class SuperWidgetFile : IDisposable
{
    private IntPtr handle;
    private WidgetFile file;

    public SuperWidgetFile(string fileName)
    {
        handle = GetGadgetHandle(fileName);
        file = new WidgetFile(fileName);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    ~SuperWidgetFile()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if(disposing)
        {
            file.Dispose();
            GC.SuppressFinalize(this);
        }

        if(handle != IntPtr.Zero)
        {
            ReleaseGadgetHandle(handle);
            handle = IntPtr.Zero;
        }
    }
}

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

Однако сборщик мусора предоставляет возможность писать код, который выполняется до того, как объект будет собран и уничтожен, и называется "финализатор". Функция, определенная как~SuperWidgetFile() является финализатором для SuperWidgetFile тип.

При использовании этого подхода реализующий класс должен:

  1. Обеспечить protected virtual void Dispose(bool disposing) функция
  2. Реализуйте интерфейс и предоставьте Dispose() функция, которая вызывает Dispose(true)
  3. Создайте финализатор, который вызывает Dispose(false)

Логическое disposingПараметр предназначен для обозначения источника вызова функции. Если он пришел из явного удаления (желаемый путь), тогда он должен оцениваться какtrue. Если он поступил от финализатора, он должен оцениватьfalse. Это потому, что, как и мы, всеIDisposableтипы, которые напрямую взаимодействуют с неуправляемыми ресурсами, должны использовать подход финализатора, и эти объекты, возможно, уже были собраны. Из - за этого, тип должен только выполнить выпуск своих собственных неуправляемых ресурсов в финализации.

Если объект размещен явно (disposing == true), тип вызывает GC.SuppressFinalize(this), который сообщает сборщику мусора, что не нужно вызывать финализатор этого объекта при его сборке.

Общие вопросы

Q: Есть IDisposableвиды "особые"? Обрабатывает ли среда выполнения.NET или сборщик мусора их по-разному, автоматически вызываяDispose, собирая их рано и т. д.?

A: Нет. В корне,IDiposableэто просто интерфейс, ничем не отличающийся от любого другого интерфейса, определенного в библиотеках.NET или в пользовательском коде. Во время выполнения объекты, реализующиеIDisposableследовать в точности те же самые правила сбора, как и все другие объекты. Единственный "особый режим".IDisposable типы существуют на уровне языка, где сокращение языка позволяет легче запоминать вызов Dispose, например, в usingконструкции в VB.NET и C#. Опять же, это только языковые функции. Они не влияют на поведение во время выполнения.

В: Всегда ли мне нужно звонитьDispose?

A: ДА. Хотя вы можете лично знать определенный тип и то, что он на самом деле делает, когдаDispose вызывается, должна быть очень четкая, четко определенная и неизбежная причина избегать вызова Disposeкогда объект больше не используется. Самый простой способ убедиться, что вы правильно утилизируете одноразовые предметы, - это поместить их вusing блоки:

using(SuperWidgetFile file = new SuperWidgetFile(@"C:\widget.wgt"))
{
    // widget code
}

Однако эта конструкция работает только тогда, когда время жизни экземпляра начинается и заканчивается в пределах одного вызова функции; в других случаях вам нужно будет убедиться, что вы звонитеDispose явно.