Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Руководство_по_C#.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
10.01 Mб
Скачать

13.6Высвобождаемые объекты

Методы финализации могут применяться для освобождения неуправляемых ресурсов при активизации процесса сборки мусора. Однако многие неуправляемые объекты являются "ценными элементами" (например, низкоуровневые соединения с базой данных или файловые дескрипторы) и часто выгоднее освобождать их как можно раньше, еще до наступления момента сборки мусора. Поэтому вместо переопределения Finalize() в качестве альтернативного варианта также можно реализовать в классе интерфейс IDisposable, который имеет единственный метод по имени Dispose():

public interface IDisposable

{

void Dispose();

}

Когда действительно реализуется поддержка интерфейса IDisposable, то предполагается, что после завершения работы с объектом метод Dispose() должен вручную вызываться пользователем этого объекта, прежде чем объектной ссылке будет позволено покинуть область действия. Благодаря этому объект может выполнять любую необходимую очистку неуправляемых ресурсов без попадания в очередь финализации и без ожидания того, когда сборщик мусора запустит содержащуюся в классе логику финализации.

Интерфейс IDisposable может быть реализован как в классах, так и в структурах (в отличие от метода Finalize(), который допускается переопределять только в классах), потому что метод Dispose() вызывается пользователем объекта (а не сборщиком мусора). Рассмотрим пример использования этого интерфейса:

using System;

namespace ConsoleApplication1

{

// Данный класс реализует интерейс IDisposable

class FinalizeObject : IDisposable

{

public int id { get; set; }

public FinalizeObject(int id)

{ this.id = id; }

// Реализуем метод Dispose()

public void Dispose()

{ Console.WriteLine("Высвобождение объекта!"); }

}

class Program

{

static void Main(string[] args)

{

FinalizeObject obj = new FinalizeObject(4);

obj.Dispose();

Console.ReadKey();

}

}

}

Обратите внимание, что метод Dispose() отвечает не только за освобождение неуправляемых ресурсов типа, но и за вызов аналогичного метода в отношении любых других содержащихся в нем высвобождаемых объектов. В отличие от Finalize(), в нем вполне допустимо взаимодействовать с другими управляемыми объектами. Объясняется это очень просто: сборщик мусора не имеет понятия об интерфейсе IDisposable и потому никогда не будет вызывать метод Dispose(). Следовательно, при вызове данного метода пользователем объект будет все еще существовать в управляемой куче и иметь доступ ко всем остальным находящимся там объектам.

Этот пример раскрывает еще одно правило относительно работы с подвергаемыми сборке мусора типами: для любого создаваемого напрямую объекта, если он поддерживает интерфейс IDisposable, следует всегда вызывать метод Dispose(). Необходимо исходить из того, что в случае, если разработчик класса решил реализовать метод Dispose(), значит, классу надлежит выполнять какую-то очистку.

Повторное использование ключевого слова using в c#

При работе с управляемым объектом, который реализует интерфейс IDisposable, довольно часто требуется применять структурированную обработку исключений, гарантируя, что метод Dispose() типа будет вызываться даже в случае возникновения какого-то исключения:

FinalizeObject obj = new FinalizeObject(4);

try

{

// Выполнение необходимых операций

}

finally

{

obj.Dispose();

}

Хотя это является замечательными примером "безопасного программирования", истина состоит в том, что очень немногих разработчиков прельщает перспектива заключать каждый очищаемый тип в блок try/finally лишь для того, чтобы гарантировать вызов метода Dispose(). Для достижения аналогичного результата, но гораздо менее громоздким образом, в C# поддерживается специальный фрагмент синтаксиса, который выглядит следующим образом:

using (FinalizeObject obj = new FinalizeObject(4))

{

// Необходимые действия

}

Если теперь просмотреть CIL-код этого метода Main() с помощью утилиты ildasm.ехе, то обнаружится, что синтаксис using в таких случаях на самом деле расширяется до логики try/finally, которая включает в себя и ожидаемый вызов Dispose() (см. рис.).

Хотя применение такого синтаксиса действительно избавляет от необходимости вручную помещать высвобождаемые объекты в рамки try/finally, в настоящее время, к сожалению, ключевое слово using в C# имеет двойное значение (поскольку служит и для добавления ссылки на пространства имен, и для вызова метода Dispose()). Тем не менее, при работе с типами .NET, которые поддерживают интерфейс IDisposable, данная синтаксическая конструкция будет гарантировать автоматический вызов метода Dispose() в отношении соответствующего объекта при выходе из блока using.

Кроме того, в контексте using допускается объявлять несколько объектов одного и того же типа. Как не трудно догадаться, в таком случае компилятор будет вставлять код с вызовом Dispose() для каждого объявляемого объекта.