Переопределение System.Object.Finalize()
Переопределение System.Object.Finalize()
В тех редких случаях, когда воздается C#-класс, использующий неуправляемые ресурсы, нужно гарантировать, что соответствующая память будет обрабатываться прогнозируемым образом. Предположим, что вы создали класс MyResourceWrapper, использующий неуправляемый ресурс (каким бы этот ресурс ни был), и вы хотите переопределить Finalize(). Немного странно, но в C# вы не можете для этого использовать ключевое слово override.
public class MyResourceWrapper {
// Ошибка компиляции!
protected override void Finalize(){}
}
Чтобы в пользовательском типе класса переопределить метод Finalize(), вы должны использовать в C# другой синтаксис деструктора, напоминающий синтаксис деструктора в C++. Причина в том, что при обработке деструктора компилятором C# в метод Finalize() автоматически добавляются необходимые элементы инфраструктуры (что будет продемонстрировано чуть позже).
Вот пример пользовательского деструктора для MyResourceWrapper, который при вызове генерирует системный сигнал. Ясно, что это сделано исключительно для примера. Настоящий деструктор должен только освобождать неуправляемые ресурсы, а не взаимодействовать с членами других управляемых o6ъeктов, поскольку вы не можете гарантировать, что эти объекты будут существовать в тот момент, когда сборщик мусора вызовет ваш метод Finalize().
// Переопределение System.Object.Finalize() с использованием
// синтаксиса деструктора.
class MyResourceWrapper {
~MyResourceWrapper() {
// Освобождение неуправляемых ресурсов.
// Завершающий сигнал (только для примера!)
Console.Веер();
}
}
Если рассмотреть этот деструктор с помощью ildasm.exe, вы увидите, что компилятор добавляет программный код контроля ошибок. Программный код вашего метода Finаlize() помещается в рамки блока try. Это делается для выявления операторов, которые во время выполнения могут сгенерировать ошибку (что формально называется исключительной ситуацией или исключением). Соответствующий блок finally гарантирует, что метод Finalize() класса будет выполнен независимо от исключений, которые могут возникать в рамках try. Формальности структурированной обработки исключений будут рассмотрены в следующей главе, а пока что проанализируйте следующее CIL-представление деструктора для C#-класса MyResourceWrapper.
.method family hidebysig virtual instance void Finalize() cil managed {
// Code size 13 (0xd)
.maxstack 1
.try {
IL_0000: ldc.i4 0x4e20
IL_0005: ldc.i4 0x3e8
IL 000a: call void [mscorlib]System.Console::Beep(int32, int32)
IL_000f: nop
IL_0010: nop
IL_0011: leave.s IL_001b
} // end.try
finally {
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::Finalize()
IL_0019: nop
IL_001a: endfinally
} // end handler
IL_001b: nop
IL_001c: ret
} // end of method MyResourceWrapper::Finalize
При тестировании типа MyResourceWrapper вы обнаружите, что завершение работы приложения сопровождается системным звуковым сигналом, поскольку среда CLR автоматически вызывает деструкторы объектов при освобождении доменов приложений.
static void Main(string[] args) {
Console.WriteLine("***** Забавы с деструкторами ***** ");
Console.WriteLine("Нажмите клавишу ввода для завершения работы");
Console.WriteLine("и вызова Finalize() сборщиком мусора");
Console.WriteLine("для объектов, предусматривающих финализацию.");
Console.ReadLine();
MyResourceWrapper rw = new MyResourceWrapper();
}
Исходный код. Проект SimpleFinalize размещен в подкаталоге, соответствующем главе 5.