1

看看这段代码:

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

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

当我尝试在类别中查找“interp”字符串时,我得到空引用异常。因此,“类别!= null”似乎不起作用。

我找到了一些建议(此处如何检查 IEnumerable 是否为空或为空?),但它们涉及使用 .Any()。但它只会使异常更早地发生(在使用 .Any() 时)。甚至 ?.Any() 都会抛出异常。

有任何想法吗?

4

4 回答 4

4

categories.Contains仅当 Categories属性为空时,此代码才会抛出 NRE 。

以下代码将抛出:

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()or 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条目,发现该属性实际上为 null 并抛出。

快速解决方法是添加一个WherebeforeSelectMany以消除 null Categories :

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

真正的解决方案是确保Categories永远不会为空 - 它应该在构造时初始化为空数组、列表等。重新分配时,绝不应将其设置为 null。

此示例将_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)就不再需要了

于 2019-03-15T08:53:46.880 回答
3

使用集合时,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;
于 2019-03-15T08:29:09.517 回答
0

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

添加 .ToList() 并且您应该通过我们在帖子中提供的信息了解更多关于错误在哪里,我们只能猜测

于 2019-03-15T08:47:22.370 回答
0

可以使用where子句并将其设为列表,然后只需检查列表中是否有任何元素

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

   }
于 2019-03-15T09:06:24.417 回答