在选择LINQ中丢失值null的最佳方法

时间:2019-05-19 08:56:18

标签: c# linq

我尝试获取IEnumerable的值,我需要修改Select的默认返回值并需要丢弃空值。

在此函数中,我为此“ .Elements()”使用XElement对象。 我需要此功能具有最佳性能,因此我在代码中使用bar

Select(x => { }..)

基本的“问题”是当我创建此代码时,我的结果是:

IE大量内容:

.Select(versiones =>
{
      var last= versiones.Elements().Last();
      if (last.condition)
      {
          var foo = bar(last);
          return new
          {
           Last = last,
           Foo = foo
          };
      }
   return null; // i need change it
})

我需要用它来明确:

0[{Last=...,Foo=...}]
1 null
2 null
3 null
4 null
5[{Last=...,Foo=...}]
...

.Where(element=> element != null)

不是最好的选择,不像我的ienum中的另一个循环。

我想返回:

.RemoveAll(null)

3 个答案:

答案 0 :(得分:2)

基本问题是,当应使用Select时,您正在使用Where来过滤元素。由于您在调用null之前不知道哪些元素可能是Select,因此最好使用:

.Select(...)
.Where(x => x != null);

请注意,与第二个示例使用RemoveAll不同,在Where内运行的过滤器将在与Select调用相同的循环中求值。

答案 1 :(得分:1)

实际上,您不需要。无论如何,集合中只有一个遍历。

为什么?

因为LINQ表达式惰性;实际上,以下代码

var l = ...;
var l2 = l.Select(i => i.ToString()).Where(i => !string.IsNullOrEmpty(i));

甚至一次都不会遍历数组!

每个LINQ运算符都返回一个IEnumerable<T>,该迭代器仅在对其进行迭代时才检索其结果。除了以下三种情况:

1)使用返回特殊集合(List<T>,数组)的LINQ运算符。例如ToList()ToArray()

2)对结果执行foreach循环,然后在请求每个项目时对其进行检索。

3)使用计数或总和运算符(仅返回一个结果的运算符),例如Count()Average()Aggerate()等。

要了解其工作原理,让我们看看Select()的可选实现:

public IEnumerable<TOutput> Select<TInput, TOutput>(this IEnumerable<TInput> source, Func<TInput, TOutput> selector)
{
    return new SelectEnumerable(source, selector);
}

神奇的事物发生在SelectEnumerable类中:

private class SelectEnumerable<TInput, TOutput> : IEnumerable<TOutput>
{
    private readonly IEnumerable<TInput> _Source;
    private readonly Func<TInput, TOutput> _Selector;

    public SelectEnumerable(IEnumerable<TInput> source, Func<TInput, TOutput> selector)
    {
        this._Source = source;
        this._Selector = selector;
    }

    public IEnumerator<TOutput> GetEnumerator()
    {
        foreach (TInput item in _Source)
        {
            yield return _Selector(item);
        }
    }

    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}

此方法有很多优点,例如:

1)很多时候,初始化代码必须快速执行。例如,在WPF应用程序中,视图模型构造器中的代码会冻结GUI,直到完成为止。因此,最好将其保存到另一个线程中。懒惰地执行您的LINQ表达式将不会花很多时间。

2)例如,当从数据库中执行代码时,检索所有行,然后将其过滤到内存中将是非常不明智的……LINQ to SQL(与其他类LINQ to Entities相似) )使用IQueryable<T>从表达式树构建SQL查询,然后将其发送到数据库。因此,尽管以下代码非常相似,但它们的效率却大不相同:

var x = db.ToList().Where(r => r.Id < 555);
// Versus
var x = db.Where(r => r.Id < 555).ToList();

第一个发送查询,例如SELECT * FROM [db],然后将结果过滤到内存中,但是第二个发送查询,例如,SELECT * FROM [db] WHERE [Id] < 555,然后将结果转换为List<T>

答案 2 :(得分:1)

您完全可以摆脱null
根据您的样本,condition需要“最后一个”元素,您可以将其简化为

var result = document.Elements()
    .Select(versiones => versiones.Elements().Last())
    .Where(last => last.condition)
    .Select(last => new
    {
        Last = last,
        Foo = bar(last)
    })
    .ToList();

您不必担心WhereSelect函数的数量,集合将只被枚举一次。