Поделиться через


"Использование шаблонов" и "использование объявлений"

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Она включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию 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 здесь.