繁体   English   中英

当集合为空时,为什么 .NET foreach 循环会抛出 NullRefException?

[英]Why does .NET foreach loop throw NullRefException when collection is null?

所以我经常遇到这种情况......其中Do.Something(...)返回一个Do.Something(...) ,如下所示:

int[] returnArray = Do.Something(...);

然后,我尝试像这样使用这个集合:

foreach (int i in returnArray)
{
    // do some more stuff
}

我只是好奇,为什么 foreach 循环不能对空集合进行操作? 对我来说,使用 null 集合执行 0 次迭代似乎是合乎逻辑的……相反,它会抛出NullReferenceException 有谁知道为什么会这样?

这很烦人,因为我使用的 API 并不清楚它们返回的内容,所以我最终得到if (someCollection != null)无处不在......

编辑:感谢大家解释foreach使用GetEnumerator并且如果没有要获取的枚举器,foreach 将失败。 我想我在问为什么语言/运行时不能或不会在获取枚举器之前进行空检查。 在我看来,这种行为仍然可以很好地定义。

嗯,简短的回答是“因为这是编译器设计者设计它的方式。” 但实际上,您的集合对象为空,因此编译器无法让枚举器循环遍历集合。

如果你真的需要做这样的事情,试试空合并运算符:

int[] array = null;

foreach (int i in array ?? Enumerable.Empty<int>())
{
   System.Console.WriteLine(string.Format("{0}", i));
}

foreach循环调用GetEnumerator方法。
如果集合为null ,则此方法调用会导致NullReferenceException

返回null是不好的做法; 您的方法应该返回一个空集合。

空集合和对集合的空引用之间存在很大差异。

当您使用foreach ,在内部,这是调用 IEnumerable 的GetEnumerator () 方法。 当引用为空时,这将引发此异常。

但是,拥有空的IEnumerableIEnumerable<T>是完全有效的。 在这种情况下,foreach 不会“迭代”任何东西(因为集合是空的),但它也不会抛出,因为这是一个完全有效的场景。


编辑:

就个人而言,如果您需要解决此问题,我会推荐一种扩展方法:

public static IEnumerable<T> AsNotNull<T>(this IEnumerable<T> original)
{
     return original ?? Enumerable.Empty<T>();
}

然后你可以调用:

foreach (int i in returnArray.AsNotNull())
{
    // do some more stuff
}

很久以前就有人回答了,但我尝试通过以下方式执行此操作,以避免空指针异常,并且可能对使用 C# 空检查运算符的人有用?。

     //fragments is a list which can be null
     fragments?.ForEach((obj) =>
        {
            //do something with obj
        });

解决此问题的另一种扩展方法:

public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
    if(items == null) return;
    foreach (var item in items) action(item);
}

以多种方式消费:

(1) 使用接受T的方法:

returnArray.ForEach(Console.WriteLine);

(2) 用表达式:

returnArray.ForEach(i => UpdateStatus(string.Format("{0}% complete", i)));

(3)用多行匿名方法

int toCompare = 10;
returnArray.ForEach(i =>
{
    var thisInt = i;
    var next = i++;
    if(next > 10) Console.WriteLine("Match: {0}", i);
});

只需编写一个扩展方法来帮助您:

public static class Extensions
{
   public static void ForEachWithNull<T>(this IEnumerable<T> source, Action<T> action)
   {
      if(source == null)
      {
         return;
      }

      foreach(var item in source)
      {
         action(item);
      }
   }
}

因为空集合与空集合不同。 空集合是没有元素的集合对象; 空集合是一个不存在的对象。

这里有一些东西可以尝试:声明两个任意类型的集合。 正常初始化一个,使其为空,并将另一个赋值为null 然后尝试向两个集合添加一个对象,看看会发生什么。

这是Do.Something()的错。 此处的最佳实践是返回大小为 0(这是可能的)而不是 null 的数组。

因为在幕后foreach获取了一个枚举器,相当于:

using (IEnumerator<int> enumerator = returnArray.getEnumerator()) {
    while (enumerator.MoveNext()) {
        int i = enumerator.Current;
        // do some more stuff
    }
}

我认为这里提供的答案对抛出异常的原因的解释非常清楚。 我只是想补充我通常处理这些系列的方式。 因为,有时,我会多次使用该集合,并且每次都必须测试是否为 null。 为了避免这种情况,我执行以下操作:

    var returnArray = DoSomething() ?? Enumerable.Empty<int>();

    foreach (int i in returnArray)
    {
        // do some more stuff
    }

这样我们就可以随心所欲地使用集合,而不必担心出现异常,并且我们不会用过多的条件语句污染代码。

使用空检查运算符?. 也是一个很好的方法。 但是,对于数组(如问题中的示例),应先将其转换为 List:

    int[] returnArray = DoSomething();

    returnArray?.ToList().ForEach((i) =>
    {
        // do some more stuff
    });
SPListItem item;
DataRow dr = datatable.NewRow();

dr["ID"] = (!Object.Equals(item["ID"], null)) ? item["ID"].ToString() : string.Empty;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM