"Использование шаблонов" и "использование объявлений"
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия записываются в соответствующих документах собраний по проектированию языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Проблема чемпиона: https://github.com/dotnet/csharplang/issues/114
Сводка
Язык добавит две новые возможности вокруг инструкции using
, чтобы упростить управление ресурсами: using
должен распознавать шаблон высвобождения в дополнение к IDisposable
и добавить в язык объявление using
.
Мотивация
Оператор using
является эффективным инструментом для управления ресурсами сегодня, но это требует достаточно много церемоний. Методы, имеющие множество ресурсов для управления, могут синтаксически запутаться из-за множества инструкций using
. Эта сложность синтаксиса такова, что большинство рекомендаций по стилю программирования содержат исключение для использования фигурных скобок в этом сценарии.
Объявление using
упрощает процесс и ставит C# наравне с другими языками, которые поддерживают блоки управления ресурсами. Кроме того, using
на основе шаблонов позволяет разработчикам расширить набор типов, которые могут участвовать здесь. Во многих случаях можно устранить необходимость создания типов оболочек, которые существуют только для использования значений в инструкции using
.
Вместе эти функции позволяют разработчикам упростить и расширить сценарии, в которых можно применить using
.
Подробный дизайн
использование объявления
Язык позволяет добавлять using
в объявление локальной переменной. Такое объявление будет иметь тот же эффект, что и объявление переменной в инструкции using
на той же позиции.
if (...)
{
using FileStream f = new FileStream(@"C:\source\using.md");
// statements
}
// Equivalent to
if (...)
{
using (FileStream f = new FileStream(@"C:\source\using.md"))
{
// statements
}
}
Время существования локального using
будет продлено до конца области видимости, в которой он объявлен. Затем локальные переменные using
будут освобождены в обратном порядке, в котором они объявлены.
{
using var f1 = new FileStream("...");
using var f2 = new FileStream("...");
using var f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
}
Нет ограничений в отношении goto
или любой другой конструкции потока управления в случае объявления using
. Вместо этого код действует так же, как и для эквивалентного акта using
.
{
using var f1 = new FileStream("...");
target:
using var f2 = new FileStream("...");
if (someCondition)
{
// Causes f2 to be disposed but has no effect on f1
goto target;
}
}
Локальная переменная, объявленная в using
локальном объявлении, будет неявно доступна только для чтения. Это соответствует поведению локальных жителей, объявленных в инструкции using
.
Грамматика языка для объявлений using
будет следующей:
local-using-declaration:
'using' type using-declarators
using-declarators:
using-declarator
using-declarators , using-declarator
using-declarator:
identifier = expression
Ограничения в отношении объявления using
:
- Может не появляться непосредственно в метке
case
, но вместо этого должен находиться внутри блока в меткеcase
. - Может не отображаться как часть объявления переменной
out
. - Должен иметь инициализатор для каждого декларатора.
- Локальный тип должен быть неявно преобразован в
IDisposable
или выполнять шаблонusing
.
Использование шаблонов
Язык введет понятие удаляемого шаблона для типов ref struct
: это ref struct
, который имеет доступный метод экземпляра Dispose
. Типы, которые соответствуют управляемому шаблону, могут участвовать в инструкции или объявлении using
, не требуя реализации IDisposable
.
ref struct Resource
{
public void Dispose() { ... }
}
using (var r = new Resource())
{
// statements
}
Это даст разработчикам возможность использовать using
для типов ref struct
. Эти типы не могут реализовывать интерфейсы в C# 8, поэтому не могут участвовать в using
операторах.
Те же ограничения из традиционного оператора using
применяются здесь также: локальные переменные, объявленные в using
, доступны только для чтения, значение null
не вызовет исключение, и т. д. Создание кода будет отличаться только в том, что не будет приведения к IDisposable
перед вызовом Dispose.
{
Resource r = new Resource();
try {
// statements
}
finally {
if (r != null) r.Dispose();
}
}
Чтобы соответствовать шаблону одноразового использования, метод Dispose
должен быть доступным членом экземпляра, иметь тип возврата void
и не содержать параметров. Это не может быть метод расширения.
Соображения
Ни в чем из этих соображений не было реализовано в C# 8
Метки регистра без блоков
using declaration
нельзя использовать непосредственно внутри метки case
из-за сложностей, связанных с фактическим временем его существования. Одно из возможных решений — просто предоставить ему такой же срок службы, как у out var
в том же месте. Было признано, что дополнительная сложность реализации функции и простота обходного решения (просто добавить блок в метку case
) не оправдывают выбор этого пути.
Будущие расширения
фиксированные локальные параметры
Оператор fixed
обладает всеми свойствами операторов using
, которые обуславливают возможность использования локальных переменных using
. Следует учитывать расширение этой функции для fixed
местных жителей. Правила времени существования и упорядочивания должны применяться в равной степени для using
и fixed
здесь.
C# feature specifications