簡體   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