Язык программирования C#9 и платформа .NET5
На заметку! Интерфейс
может быть реализован структурами неIDisposableи классами (в отличие от переопределения методаref, что допускается только для классов), т.к. методFinalize()вызывается пользователем объекта, а не сборщиком мусора. Освобождаемые структурыDispose()обсуждались в главе 4.refВ целях иллюстрации применения интерфейса
создайте новый проект консольного приложения C# по имениIDisposable. Ниже приведен модифицированный классSimpleDispose, который вместо переопределения методаMyResourceWrapperтеперь реализует интерфейсSystem.Object.Finalize():IDisposableusing 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(см. главу 6):asConsole.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.IOstatic void DisposeFileStream(){FileStream fs = new FileStream("myFile.txt", FileMode.OpenOrCreate);// Мягко выражаясь, сбивает с толку!// Вызовы этих методов делают одно и то же!fs.Close();fs.Dispose();}В то время как "закрытие" (close) файла выглядит более естественным, чем его "освобождение" (dispose), подобное дублирование методов очистки может запутывать. При работе с типами, предлагающими псевдонимы, просто помните о том, что если тип реализует интерфейс
, то вызов методаIDisposableвсегда является безопасным способом действия.Dispose()