На заметку! Интерфейс
IDisposable
может быть реализован структурами не
ref
и классами (в отличие от переопределения метода
Finalize()
, что допускается только для классов), т.к. метод
Dispose()
вызывается пользователем объекта, а не сборщиком мусора. Освобождаемые структуры
ref
обсуждались в главе 4.
В целях иллюстрации применения интерфейса
IDisposable
создайте новый проект консольного приложения C# по имени
SimpleDispose
. Ниже приведен модифицированный класс
MyResourceWrapper
, который вместо переопределения метода
System.Object.Finalize()
теперь реализует интерфейс
IDisposable
:
using System;
namespace SimpleDispose
{
// Реализация интерфейса IDisposable.
class MyResourceWrapper : <b>IDisposable</b>
{
// После окончания работы с объектом пользователь
.
// объекта должен вызывать этот метод
public void Dispose()
{
// Очистить неуправляемые ресурсы...
.
// Освободить другие освобождаемые объекты, содержащиеся внутри.
// Только для целей тестирования
Console.WriteLine("***** In Dispose! *****");
}
}
}
Обратите внимание, что метод
Dispose()
отвечает не только за освобождение неуправляемых ресурсов самого типа, но может также вызывать методы
Dispose()
для любых других освобождаемых объектов, которые содержатся внутри типа. В отличие от
Finalize()
в методе
Dispose()
вполне безопасно взаимодействовать с другими управляемыми объектами. Причина проста: сборщик мусора не имеет понятия об интерфейсе
IDisposable
, а потому никогда не будет вызывать метод
Dispose()
. Следовательно, когда пользователь объекта вызывает данный метод, объект все еще существует в управляемой куче и имеет доступ ко всем остальным находящимся там объектам. Логика вызова метода
Dispose()
прямолинейна:
using System;
using System.IO;
using SimpleDispose;
Console.WriteLine("***** Fun with Dispose *****\n");
// Создать освобождаемый объект и вызвать метод Dispose()
.
// для освобождения любых внутренних ресурсов
MyResourceWrapper rw = new MyResourceWrapper();
rw.Dispose();
Console.ReadLine();
Конечно, перед попыткой вызова метода
Dispose()
на объекте понадобится проверить, поддерживает ли тип интерфейс
IDisposable
. Хотя всегда можно выяснить, какие типы в библиотеках базовых классов реализуют
IDisposable
, заглянув в документацию, программная проверка производится с помощью ключевого слова
is
или
as
(см. главу 6):
Console.WriteLine("***** Fun with Dispose *****\n");
MyResourceWrapper rw = new MyResourceWrapper();
if (rw is IDisposable)
{
rw.Dispose();
}
Console.ReadLine();
Приведенный пример раскрывает очередное правило, касающееся управления памятью.
Правило. Неплохо вызывать метод
Dispose()
на любом создаваемом напрямую объекте, если он поддерживает интерфейс
IDisposable
. Предположение заключается в том, что когда проектировщик типа решил реализовать метод
Dispose()
, тогда тип должен выполнять какую-то очистку. Если вы забудете вызвать
Dispose()
, то память в конечном итоге будет очищена (так что можно не переживать), но это может занять больше времени, чем необходимо.
С предыдущим правилом связано одно предостережение. Несколько типов в библиотеках базовых классов, которые реализуют интерфейс
IDisposable
, предоставляют (кое в чем сбивающий с толку) псевдоним для метода
Dispose()
в попытке сделать имя метода очистки более естественным для определяющего его типа. В качестве примера можно взять класс
System.IO.FileStream
, который реализует интерфейс
IDisposable
(и потому поддерживает метод
Dispose()
), но также определяет следующий метод
Close()
, предназначенный для той же цели:
// Предполагается, что было импортировано пространство имен System.IO
static void DisposeFileStream()
{
FileStream fs = new FileStream("myFile.txt", FileMode.OpenOrCreate);
// Мягко выражаясь, сбивает с толку!
// Вызовы этих методов делают одно и то же!
fs.Close();
fs.Dispose();
}
В то время как "закрытие" (close) файла выглядит более естественным, чем его "освобождение" (dispose), подобное дублирование методов очистки может запутывать. При работе с типами, предлагающими псевдонимы, просто помните о том, что если тип реализует интерфейс
IDisposable
, то вызов метода
Dispose()
всегда является безопасным способом действия.