簡體   English   中英

Foreach 在不帶元素的 null IEnumerable 上拋出 NullReferenceException

[英]Foreach throws NullReferenceException on not null IEnumerable with elements

我在代碼中遇到了與下面代碼中類似的情況。 問題是由於某種原因在 foreach 循環中迭代會拋出NullReferenceException

我的問題是,為什么會發生這種情況?

如果我創建自己返回空元素的迭代器, foreach會處理它,並簡單地prints空行。

以下代碼的結果是test, test, NullReferenceException

using System;
using System.Collections.Generic;
using System.Linq;

public class NestedB
{
    public string Test {get;set;}
}

public class NestedA
{
    public List<NestedB> NestedCollection {get;set;}
}

public class Program
{
    public static void Main()
    {
        var listOfA = new List<NestedA>
        {
            new NestedA
            {
                NestedCollection = new List<NestedB> 
                {
                    new NestedB {Test = "test"},
                    new NestedB {Test = "test"}
                }
            },
            new NestedA ()
        };
        
        var listOfB = listOfA.SelectMany(x => x.NestedCollection);
        
        foreach (var item in listOfB)
        {
            if (item != null)
            {
                Console.WriteLine(item.Test);
            }
        }
    }
    
}

堆棧跟蹤

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at Program.Main()
Command terminated by signal 6

這就是問題:

listOfA.SelectMany(x => x.NestedCollection)

您的第二個NestedA實例沒有NestedCollection ,因此它試圖查找“null 參考中的所有項目”。 如果您手動執行此操作,您將遇到完全相同的問題:

var nestedA = new NestedA();
// This will throw an exception, because nestedA.NestedCollectoin is null
foreach (var nestedB in nestedA.NestedCollection)
{
}

最簡單的解決方法是將NestedCollection只讀屬性,但將其初始化為:

public List<NestedB> NestedCollection { get; } = new List<NestedB>();

然后,您需要修改第一個NestedA的初始化以使用集合初始化程序:

new NestedA
{
    NestedCollection =
    {
        new NestedB { Test = "test" },
        new NestedB { Test = "test" }
    }
}

如果您不想這樣做,則可以改為更改SelectMany調用:

var listOfB = listOfA.SelectMany(x => x.NestedCollection ?? Enumerable.Empty<NestedB>())

SelectMany的實現是這樣的:

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
  this IEnumerable<TSource> source,
  Func<TSource, IEnumerable<TResult>> selector)
{
  if (source == null)
    throw Error.ArgumentNull(nameof (source));
  if (selector == null)
    throw Error.ArgumentNull(nameof (selector));
  return Enumerable.SelectManyIterator<TSource, TResult>(source, selector);
}

private static IEnumerable<TResult> SelectManyIterator<TSource, TResult>(
  IEnumerable<TSource> source,
  Func<TSource, IEnumerable<TResult>> selector)
{
  foreach (TSource source1 in source)
  {
    foreach (TResult result in selector(source1)) // The error throws here
      yield return result;
  }
}

注意注釋行。 selector(source1)將為第二個NestedA項返回 null,並且此行將嘗試獲取 null 項( foreach )的枚舉器。 這就是你得到錯誤的原因。

為了補充現有的答案:

編譯優化和運行時優化可能會導致報告的行號不准確。 不僅要檢查可枚舉對象,還要檢查 foreach 主體中可能的取消引用空值,尤其是在迭代(靜態聲明的)數組時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM