【问题标题】:Null-coalescing operator in a foreach declaration using C#7使用 C#7 的 foreach 声明中的空合并运算符
【发布时间】:2017-07-04 23:41:45
【问题描述】:

我正在查看 C# 7.0 中的 this code 示例,但我不确定引擎盖下发生了什么以及此循环的性能。

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

我的问题:

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

【问题讨论】:

  • 你测试的时候对性能有什么影响?你做了测试,不是吗?请解释为什么您的测试不足以让您自己理解问题的答案。修正您的问题,使其包含一个很好的minimal reproducible example,清楚地显示您的测试方式,并详细说明您仍然无法理解您的测试的具体内容。
  • 一次。如果text 为空,则条件语句被命中一次,这应该是合乎逻辑的,无需解释。如果它正在循环,它也将是一次,因为正在循环遍历枚举器。
  • @PeterDuniho 我不确定这个问题是如何被错误地提出来的。
  • @Svek - Peter 建议您在发布之前测试代码,然后向我们展示您的测试代码、结果等,然后提出您的问题。相反,您提出了一个问题并希望我们进行测试。

标签: c# null-coalescing-operator c#-7.0


【解决方案1】:

就“foreach 的工作原理”而言,条件语句只会计算一次。

您可能想在这些问题中详细了解 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/

我认为“什么是好处”是一种基于意见的问题。
在我看来,它没有带来任何好处,只是在术语或代码可读性方面很丑。
我建议使用广泛使用的常见良好做法:

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

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

我们可能会在几年内看到空合并 + 抛出异常的使用,这将成为一个新标准:)

【讨论】:

  • 那样可能不会更好。原始代码可能是线程安全的,也可能不是线程安全的,但您的版本不是。你能解释一下为什么你说这样更好吗?
  • 根据上一段,我在“代码可读性和流程”方面说得更好。在开始时检查一个值为 null 的值或一个方法并抛出 ArgumentNullException 是一种常见的良好做法。这正是所有 .NET 类、第 3 方类和所有优秀开发人员所做的。如果每个人都开始以他们想要的方式使用代码运算符和特性,那么我们很快就会停止理解彼此的代码。短代码 != 更好。
  • 谈到线程安全,我假设text 是传递给函数的参数 - 如果我是对的,那么这段代码将始终是线程安全的。如果它是一个类成员,可以从不同的地方进行修改,那么还有许多其他技术、方法和功能可以提供线程安全。使用空合并运算符抛出新异常绝对不是其中之一。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2011-02-13
    • 2019-02-22
    • 2020-06-28
    • 2011-11-08
    • 2012-09-23
    • 2013-09-13
    • 2018-12-12
    • 1970-01-01
    • 2011-03-26
    相关资源
    最近更新 更多