Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Ниже представлена очередная версия класса

MyResourceWrapper
, который теперь является финализируемым и освобождаемым; она определена в проекте консольного приложения C# по имени
FinalizableDisposableClass
:

using System;

namespace FinalizableDisposableClass

{

// Усовершенствованная оболочка для ресурсов.

public class MyResourceWrapper : IDisposable

{

// Сборщик мусора будет вызывать этот метод, если

//
пользователь объекта забыл вызвать Dispose.

~MyResourceWrapper

{

// Очистить любые внутренние неуправляемые ресурсы.

// **Не** вызывать Dispose на управляемых объектах.

}

// Пользователь объекта будет вызывать этот метод

// для как можно более скорой очистки ресурсов.

public void Dispose

{

// Очистить неуправляемые ресурсы.

// Вызвать Dispose для других освобождаемых объектов,

// содержащихся внутри.

// Если пользователь вызвал Dispose, то финализация

// не нужна, поэтому подавить ее.

GC.SuppressFinalize(this);

}

}

}

Обратите внимание, что метод

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

Формализованный шаблон освобождения

Текущая реализация класса

MyResourceWrapper
работает довольно хорошо, но осталось еще несколько небольших недостатков. Во-первых, методы
Finalize
и
Dispose
должны освобождать те же самые неуправляемые ресурсы. Это может привести к появлению дублированного кода, что существенно усложнит сопровождение. В идеале следовало бы определить закрытый вспомогательный метод и вызывать его внутри указанных методов.

Во-вторых, желательно удостовериться в том, что метод

Finalize
не пытается освободить любые управляемые объекты, когда такие действия должен делать метод
Dispose
. В-третьих, имеет смысл также позаботиться о том, чтобы пользователь объекта мог безопасно вызывать метод
Dispose
много раз без возникновения ошибки. В настоящий момент защита подобного рода в методе
Dispose
отсутствует.

Для решения таких проектных задач в Microsoft определили формальный шаблон освобождения, который соблюдает баланс между надежностью, удобством сопровождения и производительностью. Вот окончательная версия класса

MyResourceWrapper
, в которой применяется официальный шаблон:

class MyResourceWrapper : IDisposable

{

// Используется для выяснения, вызывался ли метод Dispose.

private bool disposed = false;

public void Dispose

{

// Вызвать вспомогательный метод.

//
Указание true означает, что очистку

// запустил пользователь объекта.

CleanUp(true);

// Подавить финализацию.

GC.SuppressFinalize(this);

}

private void CleanUp(bool disposing)

{

// Удостовериться, не выполнялось ли уже освобождение

if (!this.disposed)

{

// Если disposing равно true, тогда

// освободить все управляемые ресурсы.

if (disposing)

{

// Освободить управляемые ресурсы.

}

// Очистить неуправляемые ресурсы.

}

disposed = true;

}

~MyResourceWrapper

{

// Вызвать вспомогательный метод.

// Указание false означает, что

// очистку запустил сборщик мусора.

CleanUp(false);

}

}

Обратите внимание, что в

MyResourceWrapper
теперь определен закрытый вспомогательный метод по имени
Cleanup
. Передавая ему true в качестве аргумента, мы указываем, что очистку инициировал пользователь объекта, поэтому должны быть очищены все управляемые и неуправляемые ресурсы. Однако когда очистка инициируется сборщиком мусора, при вызове методу
Cleanup
передается значение
false
, чтобы внутренние освобождаемые объекты не освобождались (поскольку нельзя рассчитывать на то, что они все еще присутствуют в памяти). И, наконец, перед выходом из
Cleanup
переменная-член
disposed
типа
bool
устанавливается в
true
, что дает возможность вызывать метод
Dispose
много раз без возникновения ошибки.

На заметку! После того как объект был "освобожден", клиент по-прежнему может обращаться к его членам, т.к. объект пока еще находится в памяти. Следовательно, в надежном классе оболочки для ресурсов каждый член также необходимо снабдить дополнительной логикой, которая бы сообщала: "если объект освобожден, то ничего не делать, а просто возвратить управление".

Чтобы протестировать финальную версию класса

MyResourceWrapper
, модифицируйте свой файл
Program.cs
, как показано ниже:

using System;

using FinalizableDisposableClass;

Console.WriteLine("***** Dispose / Destructor Combo Platter *****");

// Вызвать метод Dispose вручную, что не приводит к вызову финализатора.

MyResourceWrapper rw = new MyResourceWrapper;

rw.Dispose;

// He вызывать метод Dispose. Это запустит финализатор,

// когда объект будет обрабатываться сборщиком мусора.

Поделиться с друзьями: