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

We use cookies. Read the Privacy and Cookie Policy

Переопределение 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.