Книга: C# для профессионалов. Том II
Деструкторы
Деструкторы
C# реализует отличную от C++ модель программирования деструкторов. Это связано с тем, что механизм сборки мусора в C# предполагает следующее:
? Существует меньшая необходимость в деструкторах, так как динамически распределенная память будет удаляться автоматически.
? Так как невозможно предсказать, когда сборщик мусора реально разрушит заданный объект, то если для класса предоставляется деструктор, невозможно предсказать в точности, когда этот деструктор будет выполнен.
Поскольку память очищается в C# "за сценой", то оказывается, что только небольшое число классов действительно нуждается в деструкторах. Для тех, кому это нужно (это классы, которые поддерживают внешние неуправляемые ресурсы, такие как соединения с файлами или с базами данных), C# имеет двухэтапный механизм разрушения:
1. Класс должен выводиться из интерфейса IDisposable
и реализовывать метод Dispose()
. Этот метод предназначен для явного вызова с помощью кода клиента для указания, что он закончил работать с объектом и требуется очистить ресурсы. (Интерфейсы будет рассмотрены позже в этом приложении.)
2. Класс должен отдельно реализовать деструктор, который рассматривается как запасной механизм, на случай, если клиент не вызывает Dispose()
.
Обычная реализация Dispose()
выглядит следующим образом:
public void Dispose() {
является базовым классом, представляющим сборщика мусора.
// очистка ресурсов
System.GC.SuppressFinalize(this);
}
System.GCSuppressFinalize()
является методом, который информирует сборщика мусора, что нет необходимости вызывать деструктор для разрушаемого объекта. Вызов SuppressFinalize()
важен, так как имеется снижение производительности, если в объекте есть деструктор, который нужно вызывать в то время, когда сборщик мусора выполняет свою работу. Следствием этого является то, что реальное освобождение управляемой памяти, занимаемой этим объектом, будет существенно задерживаться.
Синтаксис деструктора по сути такой же в C#, как и в C++. Отметим, что в C# не требуется объявлять деструктор виртуальным, компилятор будет это подразумевать. Не требуется также предоставлять модификатор доступа:
Class MyClass {
~MyClass() {
// очистка ресурсов
}
}
Хотя метод Dispose()
обычно явно вызывается клиентами, C# допускает альтернативный синтаксис, который гарантирует, что компилятор примет меры, чтобы он был вызван. Если переменная объявлена внутри блока using()
, то ее область действия совпадает с блоком using
и ее метод Dispose()
будет вызываться при выходе из блока:
using (MyClass MyObject = new MyClass()) {
// код
} // MyObject.Dispose() будет неявно вызван при выходе из этого блока
Отметим, что приведенный выше код будет компилироваться успешно только если MyClass
выводится из интерфейса IDisposable
и реализует метод Dispose()
. Если нежелательно использовать синтаксис using
, то можно опустить один или оба из двух шагов в последовательности деструктора (реализация Dispose()
и реализация деструктора), но обычно реализуются оба шага. Можно также реализовать Dispose()
, без привлечения интерфейса IDisposable
, но если это делается, то снова невозможно использовать синтаксис using
, чтобы для экземпляров этого класса Dispose()
вызывался автоматически.