【问题标题】:Check for null in foreach loop在 foreach 循环中检查 null
【发布时间】: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
}

可能吗?

【问题讨论】:

标签: c# loops foreach null


【解决方案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 的回答(在“stackoverflow.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();
        }
    

    【讨论】:

      猜你喜欢
      • 2021-08-06
      • 2018-08-24
      • 2021-12-12
      • 2014-01-18
      • 2021-08-09
      • 1970-01-01
      • 2014-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多