使用 C#7 的 foreach 声明中的空合并运算符

Null-coalescing operator in a foreach declaration using C#7

我正在查看 C# 7.0 中的 this code 示例,但我不确定幕后发生的事情以及此循环的性能。

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

我的问题:

  1. 条件语句被命中一次还是多次(on 每次迭代)?
  2. 新语法看起来不一样,这样做有什么好处?

对于"how foreach works"来说,条件语句只会计算一次。

您可能想阅读有关 foreach 循环如何在这些问题中工作的更多信息:
How do foreach loops work in C#?
Does foreach evaluate the array at every iteration?

感谢 Svek 解释这是 C# 7.0 的新功能,将在 Visual Studio 2017 RC 之后发布:
http://structuredsight.com/2016/09/01/c-7-additions-throw-expressions/

我认为 "what are benefits" 是一种 opinion-based 问题。
在我看来,它没有带来任何好处,只是在术语或代码可读性方面很丑陋。
我建议使用 widely-used 常见的良好做法:

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

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

可能,我们会在几年内看到 null-coalescing + throw exception 的用法,它将成为一个新的标准:)

您应该了解 foreach 内部代码以了解此 C# 功能。 foreach 语句中表达式的右边部分必须实现 IEnumerable(<T>) 接口,整个循环在内部是一个简单的 while,像这样:

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

如您所见,此代码中有一个点可能会出现 NRE,因此您需要检查可枚举的 before 整个循环或 Enumerable extensions class,像这样:

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

// while loop here or text.SomeLinqCodeHere()

这里有些代码行并不是真正不必要的,它们添加了一些没有实际价值的熵。在简单 foreach 的情况下,它确实基于意见决定代码标准,但此功能的真正目的是将其与 C#7 中的其他新事物链接起来,例如 ?. operator,如下所示:

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 循环的性能,如前所述,它不会受到影响,因为获取枚举器只发生一次,而 before真正的循环。