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() много раз без возникновения ошибки.
На заметку! После того как объект был "освобожден", клиент по-прежнему может обращаться к его членам, т.к. объект пока еще находится в памяти. Следовательно, в надежном классе оболочки для ресурсов каждый член также необходимо снабдить дополнительной логикой, которая бы сообщала: "если объект освобожден, то ничего не делать, а просто возвратить управление".
Чтобы протестировать финальную версию класса MyResourceWrapper, модифицируйте свой файл Program.cs, как показано ниже:
using System;
using FinalizableDisposableClass;
Console.WriteLine("***** Dispose() / Destructor Combo Platter *****");
// Вызвать метод Dispose() вручную, что не приводит к вызову финализатора.
MyResourceWrapper rw = new MyResourceWrapper();
rw.Dispose();
// He вызывать метод Dispose(). Это запустит финализатор,
// когда объект будет обрабатываться сборщиком мусора.
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
MyResourceWrapper rw2 = new MyResourceWrapper();
В коде явно вызывается метод Dispose() на объекте rw, поэтому вызов деструктора подавляется. Тем не менее, мы "забыли" вызвать метод Dispose() на объекте rw2; переживать не стоит — финализатор все равно выполнится при обработке объекта сборщиком мусора.
На этом исследование особенностей управления объектами со стороны исполняющей среды через сборку мусора завершено. Хотя дополнительные (довольно экзотические) детали, касающиеся процесса сборки мусора (такие как слабые ссылки и восстановление объектов), здесь не рассматривались, полученных сведений должно быть вполне достаточно, чтобы продолжить изучение самостоятельно. В завершение главы мы взглянем на программное средство под названием ленивое (отложенное) создание объектов.
Ленивое создание объектов
При создании классов иногда приходится учитывать, что отдельная переменная-член на самом деле может никогда не понадобиться из-за того, что пользователь объекта не будет обращаться к методу (или свойству), в котором она используется. Действительно, подобное происходит нередко. Однако проблема может возникнуть, если создание такой переменной-члена сопряжено с выделением большого объема памяти.
В качестве примера предположим, что строится класс, который инкапсулирует операции цифрового музыкального проигрывателя. В дополнение к ожидаемым методам вроде Play(), Pause() и Stop() вы также хотите обеспечить возможность возвращения коллекции объектов Song (посредством класса по имени AllTracks), которая представляет все имеющиеся на устройстве цифровые музыкальные файлы.
Создайте новый проект консольного приложения по имени LazyObjectInstantiation и определите в нем следующие классы:
<b>// Song.cs</b>
<b>namespace LazyObjectInstantiation</b>
<b>{</b>
<b> // Представляет одиночную композицию.</b>
class Song
{
public string Artist { get; set; }
public string TrackName { get; set; }
public double TrackLength { get; set; }
}
}
<b>// AllTracks.cs</b>
<b>using System;</b>
<b>namespace LazyObjectInstantiation</b>
<b>{</b>
<b> // Представляет все композиции в проигрывателе.</b>
class AllTracks
{
// Наш проигрыватель может содержать
// максимум 10 000 композиций.