为什么.NET的foreach循环抛出NullRefException当收集为空?
所以我经常遇到这种情况… Do.Something(...)
返回一个Do.Something(...)
,如下所示:
int[] returnArray = Do.Something(...);
然后,我尝试像这样使用这个集合:
foreach (int i in returnArray) { // do some more stuff }
我只是好奇,为什么不能在一个空收集的foreach循环操作? 对我来说这似乎合乎逻辑,0迭代将被执行一个NullReferenceException
…相反,它会抛出一个NullReferenceException
。 任何人都知道为什么这可能是?
这是令人讨厌的,因为我正在使用那些不清楚它们返回的API,所以我最终以if (someCollection != null)
到处都是。
编辑:谢谢大家解释, foreach
使用GetEnumerator
,如果没有枚举器得到,foreach将失败。 我想我问为什么语言/运行时不能或不会做一个空检查,然后抓住枚举。 在我看来,这种行为仍然是明确的。
那么最简单的答案就是“因为编译器devise者就是这样devise的”。 实际上,你的集合对象是空的,所以编译器没有办法让枚举器遍历集合。
如果你确实需要做这样的事情,可以尝试使用null合并运算符:
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 ()方法。 当引用为空时,这将引发这个exception。
但是,有一个空的IEnumerable
或IEnumerable<T>
是完全有效的。 在这种情况下,foreach不会“迭代”任何东西(因为集合是空的),但是它也不会抛出,因为这是一个完全有效的场景。
编辑:
就个人而言,如果你需要解决这个问题,我会推荐一个扩展方法:
public static IEnumerable<T> AsNotNull<T>(this IEnumerable<T> original) { return original ?? new T[0]; }
然后你可以打电话给:
foreach (int i in returnArray.AsNotNull()) { // do some more stuff }
因为空集合与空集合不同。 一个空集合是一个没有元素的集合对象; 空集合是不存在的对象。
这里有一些尝试:声明任何types的两个集合。 正常初始化一个,使其为空,并赋值为null
。 然后尝试添加一个对象到两个集合,看看会发生什么。
另一种扩展方法来解决这个问题:
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)用expression式:
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); } } }
这是Do.Something()
的错。 这里的最佳做法是返回一个大小为0的数组(可能的)而不是null。
这是回答久了,但我已经试图做到这一点,以避免空指针exception,可能是有用的人使用C#空检查运算符?
//fragments is a list which can be null fragments?.ForEach((obj) => { //do something with obj });
因为在幕后, foreach
获得了一个统计员,相当于这个:
using (IEnumerator<int> enumerator = returnArray.getEnumerator()) { while (enumerator.MoveNext()) { int i = enumerator.Current; // do some more stuff } }
我想这里提供的答案很清楚为什么抛出exception的解释。 我只是想补充一下我通常与这些藏品合作的方式。 因为,有时候我多次使用集合,每次都必须testing是否为空。 为了避免这一点,我做了以下几点:
var returnArray = DoSomething() ?? Enumerable.Empty<int>(); foreach (int i in returnArray) { // do some more stuff }
通过这种方式,我们可以尽可能多地使用集合,而不用担心exception,并且不会用过多的条件语句来监视代码。
使用空检查运算符?.
也是一个很好的方法。 但是,在数组的情况下(如问题中的例子),应该在之前将其转换为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;