Пустая ссылка исключение после проверки на ноль (проверка на ноль не работает) - PullRequest
1 голос
/ 15 марта 2019

Посмотрите на этот код:

var categories = tokens.SelectMany(x => x.Categories);

if (categories != null)
{
    if (categories.Contains("interp")) //null ref exception
    {
        return null;
    }
}

Я получаю исключение Null Reference, когда пытаюсь найти строку interp внутри категорий. Таким образом, кажется, что "Categories! = NULL" не работает.

Я нашел несколько предложений (здесь Как проверить, является ли IEnumerable нулевым или пустым? ), но они включают использование .Any (). Но это только делает исключение более ранним (при использовании .Any ()). Даже? .Any () выдает исключение.

Есть идеи?

Ответы [ 4 ]

4 голосов
/ 15 марта 2019

Этот код будет выдавать 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) больше не требуется

3 голосов
/ 15 марта 2019

При работе с коллекциями и IEnumerable<T> избегайте использования null;если вам нечего вернуть, верните пустую коллекцию (не null).

В вашем конкретном случае SelectMany никогда не вернет null, но пустую коллекцию, поэтомуcategories != null проверка бесполезна , и вы должны проверить tokens вместо

if (null != tokens)
  // Where(x => x != null) - to be on the safe side if x == null or x.Categories == null
  if (tokens
       .Where(x => x != null && x.Categories != null)
       .SelectMany(x => x.Categories)
       .Contains("interp"))
    return null;

Однако постоянная проверка для null делает код нечитаемым ,вот почему попробуйте проверить null один раз :

// if tokens is null change it for an empty collection
tokens = tokens ?? new MyToken[0];

...

if (tokens 
      .Where(x => x != null && x.Categories != null)
      .SelectMany(x => x.Categories)
      .Contains("interp"))
    return null;
0 голосов
/ 15 марта 2019

Можно использовать предложение where и сделать его списком, а затем просто проверить, есть ли какой-либо элемент в списке

 var categories = list.Where(x => x.Categories.Contains("interp")).ToList();
 if (categories.Count() == 0)
  {
     return null;

   }
0 голосов
/ 15 марта 2019

var category = tokens.SelectMany (x => x.Categories) .ToList ();

добавьте .ToList (), и вы должны знать больше о том, где ошибка с той информацией, которую мы имеем в посте, мы можем только догадываться

...