, который теперь является финализируемым и освобождаемым; она определена в проекте консольного приложения C# по имени
FinalizableDisposableClass
:
using System;
namespace FinalizableDisposableClass
{
// Усовершенствованная оболочка для ресурсов.
public class MyResourceWrapper : IDisposable
{
// Сборщик мусора будет вызывать этот метод, если
//
пользователь объекта забыл вызвать Dispose.
~MyResourceWrapper
{
// Очистить любые внутренние неуправляемые ресурсы.
// **Не** вызывать Dispose на управляемых объектах.
}
// Пользователь объекта будет вызывать этот метод
// для как можно более скорой очистки ресурсов.
public void Dispose
{
// Очистить неуправляемые ресурсы.
// Вызвать Dispose для других освобождаемых объектов,
// содержащихся внутри.
// Если пользователь вызвал Dispose, то финализация
// не нужна, поэтому подавить ее.
GC.SuppressFinalize(this);
}
}
}
Обратите внимание, что метод
Dispose
был модифицирован для вызова метода
GC.SuppressFinalize
, который информирует исполняющую среду о том, что вызывать деструктор при обработке данного объекта сборщиком мусора больше не обязательно, т.к. неуправляемые ресурсы уже освобождены посредством логики
Dispose
.
Формализованный шаблон освобождения
Текущая реализация класса
MyResourceWrapper
работает довольно хорошо, но осталось еще несколько небольших недостатков. Во-первых, методы
Finalize
и
Dispose
должны освобождать те же самые неуправляемые ресурсы. Это может привести к появлению дублированного кода, что существенно усложнит сопровождение. В идеале следовало бы определить закрытый вспомогательный метод и вызывать его внутри указанных методов.
Во-вторых, желательно удостовериться в том, что метод
Finalize
не пытается освободить любые управляемые объекты, когда такие действия должен делать метод
Dispose
. В-третьих, имеет смысл также позаботиться о том, чтобы пользователь объекта мог безопасно вызывать метод
Dispose
много раз без возникновения ошибки. В настоящий момент защита подобного рода в методе
Dispose
отсутствует.
Для решения таких проектных задач в Microsoft определили формальный шаблон освобождения, который соблюдает баланс между надежностью, удобством сопровождения и производительностью. Вот окончательная версия класса
MyResourceWrapper
, в которой применяется официальный шаблон:
class MyResourceWrapper : IDisposable
{
// Используется для выяснения, вызывался ли метод Dispose.
private bool disposed = false;
public void Dispose
{
// Вызвать вспомогательный метод.
//
Указание true означает, что очистку
// запустил пользователь объекта.
CleanUp(true);
// Подавить финализацию.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Удостовериться, не выполнялось ли уже освобождение
if (!this.disposed)
{
// Если disposing равно true, тогда
// освободить все управляемые ресурсы.
if (disposing)
{
// Освободить управляемые ресурсы.
}
// Очистить неуправляемые ресурсы.
}
disposed = true;
}
~MyResourceWrapper
{
// Вызвать вспомогательный метод.
// Указание false означает, что
// очистку запустил сборщик мусора.
CleanUp(false);
}
}
Обратите внимание, что в
MyResourceWrapper
теперь определен закрытый вспомогательный метод по имени
Cleanup
. Передавая ему true в качестве аргумента, мы указываем, что очистку инициировал пользователь объекта, поэтому должны быть очищены все управляемые и неуправляемые ресурсы. Однако когда очистка инициируется сборщиком мусора, при вызове методу
Cleanup
передается значение
false
, чтобы внутренние освобождаемые объекты не освобождались (поскольку нельзя рассчитывать на то, что они все еще присутствуют в памяти). И, наконец, перед выходом из
Cleanup
переменная-член
disposed
типа
bool
устанавливается в
true
, что дает возможность вызывать метод
Dispose
много раз без возникновения ошибки.
На заметку! После того как объект был "освобожден", клиент по-прежнему может обращаться к его членам, т.к. объект пока еще находится в памяти. Следовательно, в надежном классе оболочки для ресурсов каждый член также необходимо снабдить дополнительной логикой, которая бы сообщала: "если объект освобожден, то ничего не делать, а просто возвратить управление".