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

Переопределение System.Object.Finalize()

В тех редких случаях, когда создается класс C#, в котором используются неуправляемые ресурсы, очевидно, понадобится обеспечить предсказуемое освобождение соответствующей памяти. Для этого необходимо переопределить метод Finalize(). Как ни странно, применять для этого ключевое слово override в C# не допускается.

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

Давайте рассмотрим пример:

using System;

namespace ConsoleApplication1

{

class FinalizeObject

{

public int id { get; set; }

public FinalizeObject(int id)

{

this.id = id;

}

// Создадим специальный деструктор

~FinalizeObject()

{

Console.WriteLine("Объект №{0} уничтожен", id);

Console.Beep();

}

}

class Program

{

static void Main(string[] args)

{

Console.Read();

// После того как будет нажата клавиша Enter (выход из программы)

// все последующие объекты будут уничтожены

FinalizeObject[] obj = new FinalizeObject[20];

for (int i = 0; i < 20; i++)

obj[i] = new FinalizeObject(i);

}

}

}

В данном примере, после завершения приложения (в данном случае консольного приложения при нажатия клавиши Enter) будет активирована сборка мусора, которая удалит оставшиеся 20 объектов класса FinalizeObject; при этом запустится деструктор.

Описание процесса финализации

Чтобы не делать лишнюю работу, следует всегда помнить, что задачей метода Finalize() является забота о том, чтобы объект .NET мог освобождать неуправляемые ресурсы во время сборки мусора. Следовательно, при создании типа, в котором никакие неуправляемые сущности не используются (так бывает чаще всего), от финализации оказывается мало толку. На самом деле, всегда, когда возможно, следует стараться проектировать типы так, чтобы в них не поддерживался метод Finalize() по той очень простой причине, что выполнение финализации отнимает время.

При размещении объекта в управляемой куче исполняющая среда автоматически определяет, поддерживается ли в нем какой-нибудь специальный метод Finalize(). Если да, тогда она помечает его как финализируемый (finalizable) и сохраняет указатель на него во внутренней очереди, называемой очередью финализации (finalization queue). Эта очередь финализации представляет собой просматриваемую сборщиком мусора таблицу, где перечислены объекты, которые перед удалением из кучи должны быть обязательно финализированы.

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

Из всего вышесказанного следует, что хотя финализация объекта действительно позволяет гарантировать способность объекта освобождать неуправляемые ресурсы, она все равно остается недетерминированной по своей природе, и по причине дополнительной выполняемой незаметным образом обработки протекает гораздо медленнее.