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

ЖАНРЫ

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

try

{

// Использовать члены rw.

}

finally

{

// Всегда вызывать Dispose, возникла ошибка или нет.

rw.Dispose;

}

Хотя это является хорошим примером защитного программирования, в действительности лишь немногих разработчиков привлекает перспектива помещения каждого освобождаемого типа внутрь блока

try/finally
, просто чтобы гарантировать вызов метода
Dispose
. Того же самого результата
можно достичь гораздо менее навязчивым способом, используя специальный фрагмент синтаксиса С#, который выглядит следующим образом:

Console.WriteLine("***** Fun with Dispose *****\n");

// Метод Dispose вызывается автоматически

// при выходе за пределы области действия using.

using(MyResourceWrapper rw = new MyResourceWrapper)

{

// Использовать объект rw.

}

Если вы просмотрите код CIL операторов верхнего уровня посредством

ildasm.exe
, то обнаружите, что синтаксис
using
на самом деле расширяется до логики
try/finally
с вполне ожидаемым вызовом
Dispose
:

.method private hidebysig static void

'<Main>$'(string[] args) cil managed

{

...

.try

{

} // end .try

finally

{

IL_0019: callvirt instance void [System.Runtime]System.IDisposable::Dispose

} // end handler

} // end of method '<Program>$'::'<Main>$'

На заметку! Попытка применения

using
к объекту, который не реализует интерфейс
IDisposable
, приводит к ошибке на этапе компиляции.

Несмотря на то что такой синтаксис устраняет необходимость вручную помещать освобождаемые объекты внутрь блоков

try/finally
, к сожалению, теперь ключевое слово
using
в C# имеет двойной смысл (импортирование пространств имен и вызов метода
Dispose
). Однако при работе с типами, которые поддерживают интерфейс
IDisposable
, такая синтаксическая конструкция будет гарантировать, что используемый объект автоматический вызовет свой метод
Dispose
по завершении блока
using
.

Кроме того, имейте в виду, что внутри

using
допускается объявлять несколько объектов одного и того же типа. Как и можно было ожидать, компилятор вставит код для вызова
Dispose
на каждом объявленном объекте:

// Использовать список с разделителями-запятыми для объявления

// нескольких объектов, подлежащих освобождению.

using(MyResourceWrapper rw = new MyResourceWrapper,

rw2 = new MyResourceWrapper)

{

// Работать с объектами rw и rw2.

}

Объявления using (нововведение в версии 8.0)

В версии C# 8.0 были добавлены объявления

using
. Объявление
using
представляет собой объявление переменной, предваренное ключевым
словом
using
. Функциональность объявления
using
будет такой же, как у синтаксиса, описанного в предыдущем разделе, за исключением явного блока кода, помещенного внутрь фигурных скобок (
{}
).

Добавьте к своему классу следующий метод:

private static void UsingDeclaration

{

// Эта переменная будет находиться в области видимости

// вплоть до конца метода.

using var rw = new MyResourceWrapper;

// Сделать что-нибудь.

Console.WriteLine("About to dispose.");

// В этой точке переменная освобождается.

}

Далее добавьте к своим операторам верхнего уровня показанный ниже вызов:

Console.WriteLine("***** Fun with Dispose *****\n");

...

Console.WriteLine("Demonstrate using declarations");

UsingDeclaration;

Console.ReadLine;

Если вы изучите новый метод с помощью

ildasm.exe
, то (вполне ожидаемо) обнаружите тот же код, что и ранее:

.method private hidebysig static

void UsingDeclaration cil managed

{

...

.try

{

...

} // end .try

finally

{

IL_0018: callvirt instance void

[System.Runtime]System.IDisposable::Dispose

...

} // end handler

IL_001f: ret

} // end of method Program::UsingDeclaration

По сути, это новое средство является "магией" компилятора, позволяющей сэкономить несколько нажатий клавиш. При его использовании соблюдайте осторожность, т.к. новый синтаксис не настолько ясен, как предыдущий.

Создание финализируемых и освобождаемых типов

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

IDisposable
и предоставить пользователю объекта способ очистки объекта по окончании работы с ним. Тем не менее, если пользователь объекта забудет вызвать метод
Dispose
, то неуправляемые ресурсы могут оставаться в памяти неопределенно долго.

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

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

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