在foreach(项目中的T项目)之前if(items!= null)是多余的吗?

Naw*_*waz 87 c# iteration foreach iterator

我经常遇到如下代码:

if ( items != null)
{
   foreach(T item in items)
   {
        //...
   }
}
Run Code Online (Sandbox Code Playgroud)

基本上,if条件确保foreach块仅在items不为null时才会执行.我想知道这种if情况是否真的需要,或者foreach是否会处理这种情况items == null.

我的意思是,我可以简单地写一下

foreach(T item in items)
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

不用担心是否items为空?是if条件多余的?或者这取决于该类型items或可能的T呢?

Vla*_*den 93

你仍然需要检查if(items!= null)否则你会得到NullReferenceException.但是你可以这样做:

List<string> items = null;  
foreach (var item in items ?? new List<string>())
{
    item.Dump();
}
Run Code Online (Sandbox Code Playgroud)

但你可能会检查它的表现.所以我仍然希望首先使用if(items!= null).

基于Eric的Lippert建议,我将代码更改为:

List<string> items = null;  
foreach (var item in items ?? Enumerable.Empty<string>())
{
    item.Dump();
}
Run Code Online (Sandbox Code Playgroud)

  • 可爱的想法; 一个空数组将是更好的,因为它消耗更少的内存并产生更少的内存压力.Enumerable.Empty <string>会更加可取,因为它会缓存它生成的空数组并重新使用它. (25认同)
  • @CodeInChaos:啊,我现在明白你的意思了.当编译器可以检测到"foreach"正在迭代List <T>或数组时,它可以优化foreach以使用值类型枚举器或实际生成"for"循环.当被迫枚举列表*或空序列*时,它必须返回到"最低公分母"codegen,这在某些情况下会更慢并产生更多的内存压力.这是一个微妙但非常好的观点.当然,故事的寓意是 - 一如既往 - 如果你有一个性能问题,那么对其进行剖析以找出真正的瓶颈是什么. (13认同)
  • 它不仅降低了空序列的枚举速度,还降低了整个序列的枚举速度.如果序列足够长则可能很重要.对于大多数代码,我们应该选择惯用代码.但是你提到的两个分配在更少的情况下会出现性能问题. (11认同)
  • @CodeInChaos:您是否经常发现枚举空序列的速度是程序中的性能瓶颈? (9认同)
  • 我希望第二个代码更慢.它将序列退化为"IEnumerable <T>",后者又降级为枚举器,使接口迭代变慢.我的测试表明在int数组上迭代的因子5降级. (3认同)

kjb*_*tel 57

使用C#6,您可以使用新的null条件运算符List<T>.ForEach(Action<T>)(或您自己的IEnumerable<T>.ForEach扩展方法).

List<string> items = null;
items?.ForEach(item =>
{
    // ...
});
Run Code Online (Sandbox Code Playgroud)

  • @Tom:假设`items`是`List &lt;T&gt;`,而不是任何`IEnumerable &lt;T&gt;`。(或者有一个自定义扩展方法,您曾说过您不希望出现这种情况。)此外,我想说的是真的不值得添加** 11条评论**,基本上都是说您喜欢某个特定的回答。 (4认同)
  • @Tom:我强烈建议您以后不要这样做。想象一下,如果每个不同意您的评论的人都将他们的评论添加到*所有您的*中。(想象一下,我已经在这里写了 11 次回复。)这根本不是 Stack Overflow 的有效使用。 (3认同)
  • 如果在 ForEach 中操作数据库,请小心不要使用它,并且更喜欢普通的 foreach。否则您将收到以下错误:在上一个操作完成之前,在此上下文上启动了第二个操作 (3认同)
  • 这是最好的解决方案,因为它不会:a) 涉及性能下降(即使不是 `null`)将整个循环泛化到 `Enumerable` 的 LCD(如使用 `??` 会),b)需要向每个项目添加扩展方法,或 c) 需要避免 `null``IEnumerable`s (Pffft!Puh-LEAZE!SMH.) 以 (cuz,`null` 表示 N/A,而空列表表示,它是应用程序,但目前,嗯,*空*!,即员工可能有佣金,对于非销售或空的销售,当他们没有赚取任何佣金时)。 (2认同)
  • 我还假设调用委托会影响性能,而不是标准的“foreach”。特别是对于我认为被转换为 `for` 循环的列表。 (2认同)

Eri*_*ert 34

这里真正的要点应该是一个序列应该首先几乎不会为空.只需在所有程序中使其成为一个不变量,如果你有一个序列,它永远不会为空.它始终初始化为空序列或其他一些真正的序列.

如果序列永远不为null,那么显然你不需要检查它.

  • 当然,除非null和空意味着完全不同的东西.有时这对序列有效. (6认同)
  • @Nawaz:如果我有一个WCF服务返回我的空序列,意味着它们是空序列,那么我会把它作为一个bug报告给他们.这就是说:如果你必须处理可能有缺陷的服务的错误输出,那么是的,你必须通过检查null来处理它. (4认同)
  • 如果您从 WCF 服务获取序列呢?它可能是空的,对吧? (2认同)
  • @JasonMasters:那么为什么 C# 8 要做大量的工作来确保您可以在语言和框架中表达“此引用永远不为空”?需要空值检查的想法已经偏离了轨道:**如果引用永远不为空,那么你不需要空值检查**。这是正确的解决方案:禁止空引用。不鼓励他们!正是这种态度“需要”如此多的空检查。 (2认同)

Teo*_*gul 10

实际上@Connect上有一个功能请求:http://connect.microsoft.com/VisualStudio/feedback/details/93497/foreach-should-check-for-null

而且反应非常符合逻辑:

我认为大多数foreach循环都是为了迭代非null集合而编写的.如果你尝试迭代null,你应该得到你的异常,以便你可以修复你的代码.


nbz*_*nbz 5

你总是可以用空列表来测试它...但这是我在msdn网站上找到的

foreach-statement:
    foreach   (   type   identifier   in   expression   )   embedded-statement 
Run Code Online (Sandbox Code Playgroud)

如果expression的值为null,则抛出System.NullReferenceException.