Этот код будет выдавать NRE в categories.Contains
, только если свойство Categories равно нулю.
Будет выдан следующий код:
class Token
{
public string[] Categories{get;set;}
}
var tokens=new []{new Token()};
var categories = tokens.SelectMany(x => x.Categories);
if (categories != null)
{
if (categories.Contains("interp"))
{
Console.WriteLine("Found");
}
}
Но так было бы
tokens.SelectMany(x => x.Categories).ToArray();
На самом деле бросает вложенный итератор внутри SelectMany , а не ToArray()
или Contains
. Трассировка стека для этого исключения:
at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value, IEqualityComparer`1 comparer)
at UserQuery.Main()
SelectMany
попытается перебрать каждую запись Categories
, обнаружит, что свойство на самом деле равно нулю, и выбросить.
Быстрое решение - добавить Where
перед SelectMany
, чтобы исключить ноль Категории:
var categories = tokens.Where(x=>x.Categories!=null).SelectMany(x => x.Categories);
Решение real состоит в том, чтобы гарантировать, что Categories
никогда не будет пустым - его следует инициализировать пустым массивом, списком, чем угодно при создании. При переназначении он никогда не должен быть установлен на ноль.
В этом примере для поля _categories
устанавливается значение new string[0]
, даже если вызывающий абонент передает null
в категории
.
class Token
{
string[] _categories=new string[0];
public string[] Categories{
get => _categories;
set => _categories = value??new string[0];
}
}
При этом Where(x=>x.Categories !=null)
больше не требуется