在 foreach 循环中检查 null

Posted

技术标签:

【中文标题】在 foreach 循环中检查 null【英文标题】:Check for null in foreach loop 【发布时间】:2012-07-28 21:09:06 【问题描述】:

是否有更好的方法来执行以下操作: 在继续循环之前,我需要在 file.Headers 上检查 null

if (file.Headers != null)

  foreach (var h in file.Headers)
  
   //set lots of properties & some other stuff
  

简而言之,由于我的代码中发生的缩进级别,在 if 中编写 foreach 看起来有点难看。

会评估为

foreach(var h in (file.Headers != null))

  //do stuff

可能吗?

【问题讨论】:

你可以看这里:***.com/questions/6937407/… ***.com/questions/872323/… 是另一个想法。 @AdrianFaciu 我认为这完全不同。该问题在执行 for-each 之前首先检查集合是否为空。您的链接检查集合中的项目是否为空。 类似于***.com/q/3088147/80161 和***.com/q/6455311/80161 C# 8 可以简单地具有某种空条件 foreach,即这样的语法:foreach? (var i in collection) 我认为这是一个足够常见的场景来证明这一点,并且鉴于最近对语言的空条件添加,它在这里有意义吗? 【参考方案1】:

作为对 Rune 建议的轻微修饰,您可以创建自己的扩展方法:

public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)

    return source ?? Enumerable.Empty<T>();

然后你可以写:

foreach (var header in file.Headers.OrEmptyIfNull())


根据口味改名:)

【讨论】:

【参考方案2】:

假设 file.Headers 中元素的类型是 T 你可以这样做

foreach(var header in file.Headers ?? Enumerable.Empty<T>())
  //do stuff

如果 file.Headers 为空,这将创建一个空的 T 枚举。但是,如果文件类型是您拥有的类型,我会考虑更改 Headers 的 getter。 null 是 unknown 的值,所以如果可能,当 null 实际上(/最初)应该解释为“我不知道是否有任何元素”时,而不是使用 null 作为“我知道没有元素”,而是使用空设置以表明您知道集合中没有元素。这也将是 DRY'er,因为您不必经常进行 null 检查。

EDIT 作为 Jons 建议的后续,您还可以创建一个扩展方法,将上述代码更改为

foreach(var header in file.Headers.OrEmptyIfNull())
  //do stuff

在你不能改变getter的情况下,这将是我自己的首选,因为它通过给操作命名(OrEmptyIfNull)更清楚地表达了意图

上面提到的扩展方法可能会使优化器无法检测到某些优化。具体来说,那些与 IList 相关的使用方法重载 this 可以消除

public static IList<T> OrEmptyIfNull<T>(this IList<T> source)

    return source ?? Array.Empty<T>();

【讨论】:

@kjbartel 的回答(在“***.com/a/32134295/401246”是最好的解决方案,因为它不会:a)涉及性能下降(即使不是null)退化IEnumerable&lt;T&gt; 到 LCD 的整个循环(如使用 ?? 会),b)需要向每个项目添加扩展方法,或 c)需要避免 null IEnumerables(Pffft!Puh-LEAZE!SMH。)以 (cuz null 表示 N/A,而空列表表示,它适用,但目前,嗯,为空!,即员工可能有非销售的 N/A 的佣金或销售空的佣金,但他们没有' t 赚取任何)。 @Tom 除了 one 空检查之外,对于枚举数不为空的情况没有任何惩罚。在确保可枚举不为空的同时避免该检查是不可能的。上面的代码需要 IEnumerable 的标头,这比 foreach 要求更严格,但比您链接到的答案中List&lt;T&gt; 的要求更严格。测试可枚举是否为空的性能损失相同。 我将“LCD”问题基于 Eric Lippert 对 Vlad Bezden 的回答与 kjbartel 的回答在同一线程中的评论:“@CodeInChaos:啊,我现在明白你的意思了。当编译器可以检测到“foreach”正在迭代 List 或数组,然后它可以优化 foreach 以使用值类型枚举器或实际生成“for”循环。当强制枚举列表或空序列时它必须回到“最小公分母”代码生成,在某些情况下可能会更慢并产生更大的内存压力......”。同意它需要List&lt;T&gt; @tom 答案的前提是 file.Headers 是一个 IEnumerable 在这种情况下编译器无法进行优化。然而,扩展扩展方法解决方案以避免这种情况是相当直接的。见编辑【参考方案3】:

坦率地说,我建议:接受null 测试。 null 测试只是 brfalsebrfalse.s;其他一切都将涉及更多工作(测试、分配、额外的方法调用、迭代器上不必要的GetEnumerator()MoveNext()Dispose() 等)。

if 测试简单、明显且高效。

【讨论】:

您确实提出了一个有趣的观点 Marc,但是,我目前正在寻求减少代码的缩进级别。但是当我需要记录性能时,我会记住您的评论。 在我提出这个问题并需要实施一些性能增强多年后,就这个 Marc 做一个简短的说明,您的建议非常方便。谢谢【参考方案4】:

使用Null-conditional Operator 和 ForEach() 比标准的 foreach 循环更快。 不过,您必须将集合转换为 List。

   listOfItems?.ForEach(item => // ... );

【讨论】:

请在您的答案周围添加一些解释,明确说明此解决方案为何有效,而不仅仅是代码单行 最适合我的情况【参考方案5】:

迭代前的“if”没问题,那些“漂亮”的语义很少会降低您的代码的可读性。

无论如何,如果缩进打扰了你,你可以改变 if 来检查:

if(file.Headers == null)  
   return;

只有当 headers 属性有一个真值时,你才会进入 foreach 循环。

我可以考虑的另一个选项是在 foreach 循环中使用 null-coalescing 运算符并完全避免 null 检查。 示例:

List<int> collection = new List<int>();
collection = null;
foreach (var i in collection ?? Enumerable.Empty<int>())

    //your code here

(将集合替换为您的真实对象/类型)

【讨论】:

如果if语句之外有任何代码,您的第一个选项将不起作用。 我同意,与创建新列表相比,if 语句实现起来简单且便宜,只是为了美化代码。【参考方案6】:

我正在为这些场景使用一个不错的小扩展方法:

  public static class Extensions
  
    public static IList<T> EnsureNotNull<T>(this IList<T> list)
    
      return list ?? new List<T>();
    
  

鉴于 Headers 是列表类型,您可以执行以下操作:

foreach(var h in (file.Headers.EnsureNotNull()))

  //do stuff

【讨论】:

您可以使用?? 运算符并将return 语句缩短为return list ?? new List&lt;T&gt;; @wolfgangziegler,如果我理解正确,您的样本file.Headers.EnsureNotNull() != null 中的null 测试是不需要的,甚至是错误的?【参考方案7】:

在某些情况下,我更喜欢另一种通用变体,假设默认集合构造函数通常返回空实例。

最好将此方法命名为NewIfDefault。它不仅对集合有用,所以类型约束IEnumerable&lt;T&gt; 可能是多余的。

public static TCollection EmptyIfDefault<TCollection, T>(this TCollection collection)
        where TCollection: class, IEnumerable<T>, new()
    
        return collection ?? new TCollection();
    

【讨论】:

以上是关于在 foreach 循环中检查 null的主要内容,如果未能解决你的问题,请参考以下文章

C# Foreach 循环 - 继续问题

如何在 foreach 循环中删除数组元素?

等待 forEach 循环

forEach 循环中的 AJAX 函数

Dictionary.ContainsKey / Value与检查某个键/值的foreach循环之间的速度是否存在差异

在 PHP 中使用 foreach 循环时查找数组的最后一个元素