Правило.
Единственная серьезная причина для переопределения методаFinalize() связана с использованием в классе C# неуправляемых ресурсов через P/Invoke или сложные задачи взаимодействия с СОМ (обычно посредством разнообразных членов типа System.Runtime.InteropServices.Marshal). Это объясняется тем, что в таких сценариях производится манипулирование памятью, которой исполняющая среда управлять не может.Переопределение метода System.Object.Finalize()
В том редком случае, когда строится класс С#, в котором применяются неуправляемые ресурсы, вы вполне очевидно захотите обеспечить предсказуемое освобождение занимаемой памяти. В качестве примера создадим новый проект консольного приложения C# по имени SimpleFinalize
MyResourceWrapper, в котором используется неуправляемый ресурс (каким бы он ни был). Теперь необходимо переопределить метод Finalize(). Как ни странно, для этого нельзя применять ключевое слово override языка С#:using System;
namespace SimpleFinalize
{
class MyResourceWrapper
{
// Compile-time error!
protected override void Finalize(){ }
}
}
На самом деле для обеспечения того же самого эффекта используется синтаксис деструктора (подобный C++). Причина такой альтернативной формы переопределения виртуального метода заключается в том, что при обработке синтаксиса финализатора компилятор автоматически добавляет внутрь неявно переопределяемого метода Finalize()
Финализаторы C# выглядят похожими на конструкторы тем, что именуются идентично классу, в котором определены. Вдобавок они снабжаются префиксом в виде тильды (~
MyResourceWrapper, который при вызове выдает звуковой сигнал. Очевидно, такой пример предназначен только для демонстрационных целей. В реальном приложении финализатор только освобождает любые неуправляемые ресурсы и не взаимодействует с другими управляемыми объектами, даже с теми, на которые ссылается текущий объект, т.к. нельзя предполагать, что они все еще существуют на момент вызова этого метода Finalize() сборщиком мусора.using System;
// Переопределить System.Object.Finalize()
// посредством синтаксиса финализатора.
class MyResourceWrapper
{
// Очистить неуправляемые ресурсы.
// Выдать звуковой сигнал при уничтожении
// (только в целях тестирования)
~MyResourceWrapper() => Console.Beep();
}
Если теперь просмотреть код CIL данного финализатора с помощью утилиты ildasm.exe
Finalize() помещены в блок try (см. главу 7). Связанный с ним блок finally гарантирует, что методы Finalize() базовых классов будут всегда выполняться независимо от любых исключений, возникших в области try..method family hidebysig virtual instance void
Finalize() cil managed
{
.override [System.Runtime]System.Object::Finalize
// Code size 17 (0x11)
.maxstack 1
.try
{
IL_0000: call void [System.Console]System.Console::Beep()
IL_0005: nop
IL_0006: leave.s IL_0010
} // end .try
finally
{
IL_0008: ldarg.0
IL_0009: call instance void [System.Runtime]System.Object::Finalize()
IL_000e: nop
IL_000f: endfinally
} // end handler
IL_0010: ret
} // end of method MyResourceWrapper::Finalize
Тестирование класса MyResourceWrapper
using System;
using SimpleFinalize;
Console.WriteLine("***** Fun with Finalizers *****\n");
Console.WriteLine("Hit return to create the objects ");