Обнуляемый оператор в объявлении foreach с использованием C#7

Я смотрел на этот пример кода в C# 7.0 и я не был уверен в том, что происходило под капотом и производительности этой петли.

foreach (var c in text ?? throw new ArgumentNullException(nameof(text)))
{
    ...
}

Мои вопросы:

  1. Условный оператор получает удар один или несколько раз (на каждой итерации)?
  2. Новый синтаксис выглядит иначе, каковы преимущества этого?

2 ответа

Решение

Ты должен понимать foreach внутренний код для понимания этого C# особенность. Правая часть выражения в foreach заявление должно реализовать IEnumerable(<T>) интерфейс, и весь цикл, внутри, простой while, что-то вроде этого:

// here can be NullReferenceException
var en = text.GetEnumerator();
while(en.MoveNext())
{
    var c = en.Current;
    {
        ...
    }
}

Как видите, в этом коде есть точка NRE может произойти, поэтому вам нужно проверить перечислимое перед целым циклом или Enumerable класс расширений, вот так:

if (text.IsNullOrWhitespace())
{
    throw new ArgumentNullException(nameof(text));
}

// while loop here or text.SomeLinqCodeHere()

Здесь есть несколько строк кода, которые на самом деле не являются ненужными, добавляя некоторую энтропию без реальной ценности. В случае простого foreach это действительно основанное на мнении решение о стандартах кода, но реальная цель этой функции - связать ее с другими новыми вещами в C#7, лайк ?. оператор, как это:

int? length = customers?.Length ?? throw new ...;
Customer first = customers?[0] ?? throw new ...;  
int? count = customers?[0]?.Orders?.Count() ?? throw new ...;

В таких случаях выдача исключения аналогична комментарию в конце строки кода:

int? length = customers?.Length; // should not be null
Customer first = customers?[0]; // should not be null  
int? count = customers?[0]?.Orders?.Count(); // should not be null

но он добавляет некоторые строгие контрактные правила для вашего кода.

Что касается производительности для foreach Цикл с такими выражениями, как уже было сказано, не страдает, так как получение перечислителя происходит только один раз, и до реального цикла.

В терминах "как работает foreach" условное утверждение будет рассчитываться только один раз.

Вы можете прочитать больше о том, как работают циклы foreach в следующих вопросах:
Как работают циклы foreach в C#?
Оценивает ли foreach массив на каждой итерации?

Спасибо Svek за объяснение, что это новая функция C# 7.0, которая будет выпущена после Visual Studio 2017 RC:
http://structuredsight.com/2016/09/01/c-7-additions-throw-expressions/

Я думаю, что "что такое выгода" - это своего рода вопрос, основанный на мнении.
На мой взгляд, это не приносит ничего хорошего и просто уродливо с точки зрения или читабельности кода.
Я бы рекомендовал использовать широко распространенную общепринятую практику:

if (text == null) // or string.IsNullOrEmpty for strings
    throw new ArgumentNullException(nameof(text));

foreach (var c in text)
{
    // ...
}

Возможно, через пару лет мы увидим использование исключения null-coalescing + throw, и оно станет новым стандартом:)

Другие вопросы по тегам