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


103

我经常遇到如下代码:

if ( items != null)
{
   foreach(T item in items)
   {
        //...
   }
}

基本上,该if条件确保foreach仅在items不为null 的情况下才执行块。我想知道是否if确实需要该条件,或者如果满足,foreach将解决该情况items == null

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

foreach(T item in items)
{
    //...
}

不用担心是否items为null?是if条件多余的?还是取决于类型items或者取决于T



1
@ kjbartel的回答(在“ stackoverflow.com/a/32134295/401246 ”是最好的解决方案,因为它没有:一)涉及的(即使没有性能下降null)整个循环推广到的LCD Enumerable(使用??会),b)要求向每个项目添加扩展方法,或c)要求避免以null IEnumerables(Pffft!Puh-LEAZE!SMH。)开头(cuz,null表示不适用,而空列表表示,它是适用的,但是目前,好吧!为,即Empl。的佣金对于非销售人员而言为N / A,对于没有销售的佣金而言则为空)。
汤姆(Tom)

Answers:


115

您仍然需要检查(items!= null),否则将获得NullReferenceException。但是,您可以执行以下操作:

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

但您可以检查其性能。所以我仍然更喜欢首先使用if(items!= null)。

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

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

31
可爱的主意;空数组将是更可取的,因为它消耗更少的内存并产生更少的内存压力。Enumerable.Empty <string>会更可取,因为它会缓存生成的空数组并重新使用它。
埃里克·利珀特

5
我希望第二个代码会慢一些。它将序列退化为IEnumerable<T>,反过来又退化为对接口的枚举数,从而使迭代变慢。我的测试表明,在int数组上进行迭代的系数下降了5。
CodesInChaos 2011年

11
@CodeInChaos:您通常会发现枚举空序列的速度是程序的性能瓶颈吗?
埃里克·利珀特

14
它不仅降低了空序列的枚举速度,还降低了整个序列的枚举速度。如果序列足够长,可能会很重要。对于大多数代码,我们应该选择惯用代码。但是您提到的两个分配在更少的情况下将是一个性能问题。
CodesInChaos 2011年

15
@CodeInChaos:啊,我明白你的意思了。当编译器可以检测到“ foreach”正在遍历List <T>或数组时,它可以优化foreach以使用值类型的枚举器或实际生成“ for”循环。当被迫枚举一个列表或空序列时,它必须返回到“最低公分母”码本,在某些情况下它可能会变慢并产生更大的内存压力。这是一个微妙但很好的观点。当然,故事的寓意是-如往常一样-如果您遇到性能问题,则可以对其进行概要分析以找出真正的瓶颈。
埃里克·利珀特

68

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

List<string> items = null;
items?.ForEach(item =>
{
    // ...
});

优雅的答案。谢谢!
史蒂夫

2
这是最好的解决方案,因为它不会:a)导致性能下降(即使不这样做null),将整个循环推广到LCD上Enumerable(如使用??),b)需要向每个项目添加扩展方法,或者c )要求避免以null IEnumerables(Pffft!Puh-LEAZE!SMH。)开头(cuz,null表示N / A,而空列表表示它是适用的,但目前是空的!),即一个Empl。可能具有对于非销售人员,则为N / A;对于尚未获得收入的销售人员,则为空)。
汤姆(Tom)

6
@Tom:它假定items是一个List<T>尽管,而不只是任何一个IEnumerable<T>。(或者有一个自定义的扩展方法,您曾说过您不希望出现这种情况。)此外,我想说的是真的不值得添加11条评论,而这些评论基本上都是在说您喜欢特定的答案。
乔恩·斯基特

2
@汤姆:我强烈建议您以后不要这样做。想象一下,如果每个不同意您的评论的人都将其评论添加到您的所有评论中。(想象一下,我在这里写了我的答复,但有11次。)这根本不是对Stack Overflow的有效利用。
乔恩·斯基特

1
我还假设调用委托与标准相比会对性能产生影响foreach。特别是对于我认为会转换为for循环的列表。
kjbartel

37

真正的收获应该是一个序列,首先应该几乎不为null。只需使它在所有程序中不变即可,如果您有序列,则它永远不会为null。它始终被初始化为空序列或其他一些真实序列。

如果一个序列永远不会为空,那么显然您不需要检查它。


1
如果从WCF服务获取序列怎么办?它可能为空,对吗?
Nawaz

4
@Nawaz:如果我有一个WCF服务,该服务返回了空序列,使它们成为空序列,那么我会将其作为错误报告给他们。就是说:如果您必须处理格式错误的服务(可能是错误的服务)的输出,那么是的,您必须通过检查null来处理它。
埃里克·利珀特

7
当然,除非null和empty意味着完全不同的事物。有时这对序列有效。
配置器

@Nawaz返回空值而不是空集合的DataTable.Rows怎么样。也许是一个错误?
尼尔B

@ kjbartel的回答(在“ stackoverflow.com/a/32134295/401246 ”是最好的解决方案,因为它没有:一)涉及的(即使没有性能下降null)整个循环推广到的LCD Enumerable(使用??会),b)要求向每个项目添加扩展方法,或c)要求避免以null IEnumerables(Pffft!Puh-LEAZE!SMH。)开头(cuz,null表示不适用,而空列表表示,它是适用的,但是当前,好吧!为,即,示例可能会为非销售人员提供不适用的佣金,或者在没有收入时向销售人员提供佣金)。
汤姆(Tom)

10

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

响应非常合乎逻辑:

我认为大多数foreach循环都是出于迭代非null集合的目的而编写的。如果尝试通过null进行迭代,则应获取异常,以便可以修复代码。


我想这是有利有弊,因此他们决定保留它的设计初衷。毕竟,foreach只是一些语法糖。如果您调用了items.GetEnumerator(),即使item为null,它也会崩溃,因此您必须先对其进行测试。
Marius Bancila

6

您总是可以使用空列表进行测试...但这就是我在msdn网站上找到的内容

foreach-statement:
    foreach   (   type   identifier   in   expression   )   embedded-statement 

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


2

它不是多余的。在运行时,项目将强制转换为IEnumerable,并将调用其GetEnumerator方法。这将导致取消引用将失败的项目


1
1)序列不一定要强制转换为IEnumerable2)这是一个设计决策,需要对其进行抛出。null如果开发人员认为这是个好主意,C#可以轻松地插入该检查。
CodesInChaos 2011年

2

您可以将null检查封装在扩展方法中,并使用lambda:

public static class EnumerableExtensions {
  public static void ForEach<T>(this IEnumerable<T> self, Action<T> action) {
    if (self != null) {
      foreach (var element in self) {
        action(element);
      }
    }
  }
}

代码变为:

items.ForEach(item => { 
  ...
});

如果您只想调用一个接受项并返回的方法,则可以更加简洁void

items.ForEach(MethodThatTakesAnItem);

1

您确实需要这个。当foreach访问容器以其他方式设置迭代时,您将获得异常。

在幕后,foreach使用在集合类实现的接口来执行迭代。通用等效接口在这里

C#语言的foreach语句(对于Visual Basic中的每个语句)隐藏了枚举数的复杂性。因此,建议使用foreach而不是直接操作枚举器。


1
就像它在技术上不使用该接口一样,它使用鸭子输入:blogs.msdn.com/b/kcwalina/archive/2007/07/18/ducknotation.aspx这些接口确保存在正确的方法和属性。但是,有助于理解意图。以及在foreach外部使用...
ShuggyCoUk 2011年

0

该测试是必需的,因为如果集合为null,则foreach将抛出NullReferenceException。试用起来其实很简单。

List<string> items = null;
foreach(var item in items)
{
   Console.WriteLine(item);
}

0

第二个将抛出一条NullReferenceException消息Object reference not set to an instance of an object.


0

如此处所述您需要检查它是否不为空。

不要使用计算结果为null的表达式。


0

在C#6中,您可以这样写:

// some string from file or UI, i.e.:
// a) string s = "Hello, World!";
// b) string s = "";
// ...
var items = s?.Split(new char[] { ',', '!', ' ' }) ?? Enumerable.Empty<string>();  
foreach (var item in items)
{
    //..
}

基本上是Vlad Bezden的解决方案,但使用?? 表达式始终生成不为null的数组,因此可以在foreach中生存,而不是在foreach括号内进行此检查。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.