Повторное использование ключевого слова using в C#
Имея дело с управляемым объектом, который реализует интерфейс
IDisposable
, довольно часто приходится применять структурированную обработку исключений, гарантируя тем самым, что метод
Dispose()
типа будет вызываться даже в случае генерации исключения во время выполнения:
Console.WriteLine("***** Fun with Dispose *****\n");
MyResourceWrapper rw = new MyResourceWrapper ();
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