Неуправляемые ресурсы и освобождение ранее выделенной области памяти
Предположим, что некоторый объект, с помощью которого был открыт файл, программе больше не нужен и помечен для сборки мусора. В конечном счете будет вызван деструктор объекта, при выполнении которого файл может быть закрыт. Но, как мы уже обсуждали, сборка мусора – процесс недетерминированный (во времени, во всяком случае), и файл может оставаться открытым неопределенно долго. Более эффективным было бы иметь в клиентской программе детерминированный механизм освобождения ресурсов объекта, который стал ненужным. Каркас .NET Framework рекомендует для этой цели использовать интерфейс IDisposable.
public _gc _interface IDisposable // сборщик мусора – интерфейс IDisposable { void Dispose(); };
В этом шаблоне проектирования определяется, что клиентская программа должна вызывать Dispose (Освободить ранее выделенную область памяти) для объекта, когда исчезает необходимость в этом объекте. Метод Dispose (Освободить ранее выделенную область памяти) реализуется так, что класс выполняет все необходимые действия по очистке. Для полной гарантии, в классе нужно реализовать и деструктор – на случай, если метод Dispose (Освободить ранее выделенную область памяти) никогда не будет вызван, возможно, из-за появления исключения.
Так как и метод Dispose (Освободить ранее выделенную область памяти), и деструктор производят очистку, код, освобождающий ресурсы, может быть помещен в Dispose (Освободить ранее выделенную область памяти), а в деструкторе можно просто вызывать Dispose (Освободить ранее выделенную область памяти). Метод Dispose (Освободить ранее выделенную область памяти) разработан так, что клиентская программа может вызывать его при завершении работы с объектом или когда будет известно, что нет никакой опасности в освобождении ресурсов, связанных с объектом.
Отметим, что недопустимо вызывать деструктор объекта после вызова метода Dispose (Освободить ранее выделенную область памяти), потому что это приведет к повторной очистке. Объект может быть удален из очереди сборки мусора с помощью GC::SuppressFinalize. Кроме того, полезно реализовать в классе булев флажок, назвав его, например disposeCalled. Если Dispose (Освободить ранее выделенную область памяти) вызывается дважды, проверка этого флажка предотвратит повторное выполнение очистки.
Метод Dispose (Освободить ранее выделенную область памяти) должен также вызвать метод Dispose (Освободить ранее выделенную область памяти) базового класса с целью удостовериться, что все его ресурсы тоже освобождаются. Причем и этот метод должен быть написан так, чтобы не возникала исключительная ситуация при его вызове, если ресурсы уже были освобождены.
Поскольку завершение представляет собой дорогой процесс, любой объект, который больше не будет использовать ресурсы, должен вызвать статический метод GC::SupressFinalize, передавая ему указатель this. При наличии в коде блока try/finally можно разместить вызов метода Dispose (Освободить ранее выделенную область памяти) объекта в блоке finally (наконец), чтобы удостовериться в том, что ресурсы будут освобождены.
Пример программы DisposeDemo иллюстрирует шаблон для освобождения ресурсов. Класс SimpleLog с помощью класса StreamWriter реализует запись (информации) в файл.
//SimpleLog.h using namespace System; // использование пространства имен Система; using namespace System::10; // использование пространства имен Система::10; public _gc class SimpleLog: public IDisposable // класс сборщика мусора SimpleLog: IDisposable { private: // частный StreamWriter *writer; String *name; // Строка bool disposeCalled; // логический (булев) флажок disposeCalled public: SimpleLog(String *fileName): disposeCalled(false) // (Строка *fileName): (ложь) { name = fileName; // имя файла writer = new StreamWriter(fileName, false); // устройство записи = новый StreamWriter (имя // файла, ложь); writer › AutoFlush = true; // устройство записи › Автосброс = истина; Console::WriteLine( String::Format("logfile {0} created", name)); // Строка::Формат ("системный журнал (0} // создан", имя)); } void WriteLine(String *str) // Строка { writer › WriteLine(str); // запись Console::WriteLine(str); } void Dispose () { if(disposeCalled) // если (disposeCalled) – если все уже сделано return; writer › Close (); GC::SuppressFinalize (this); // СБОРЩИК МУСОРА Console::WriteLine( String::Format("logfile {0} disposed", name)); // Строка::Формат ("системный журнал {0} // закрыт ", имя)); disposeCalled = true; // истина – все уже сделано } – SimpleLog() { Console::WriteLine( String::Format("logfile {0} finalized", name)); // Строка::Формат ("системный журнал (0) // завершен", имя)); Dispose(); } };