我怎样才能改进 LINQ?

How can I improve LINQ?

所以这个问题是关于静态集合的 .Select() 语句(即不是 Select()Where() 或其他 LINQ 操作的结果,例如 List array).

我的印象是,当使用 .Select() 或其他非过滤、非排序方法时,.ElementAt() 会从原始集合中取出元素,然后 运行通过.Select。我认为这是最好的方法,因为 .ElementAt() 只有 returns 一个元素,LINQ 不缓存任何东西,所以其他生成的项目被丢弃。

举个例子:

var original = Enumerable.Range(0, 1000);

var listWithADifficultSelect = original.Select(aMethod);

var onlyOneItem = listWithADifficultSelect.ElementAt(898);

object aMethod(int number) {
    // Gets the item from some kind of database, difficult operation
    // Takes at least a few milliseconds
    return new object();
}

从更大的角度来看,如果我有一个包含 20K 个项目的列表,我只需要第 nth 个项目,但我执行了相当繁重的 .Select(), 我希望 .Select() 仅投影列表中的一项。

所以我有两个问题:

  1. 为什么要这样建?
  2. 有没有办法构建一个改进的 .Select() 来满足我的要求?

如果我对你的问题的理解正确,如果你只需要第 898 个元素,你不希望 LINQ 为前 897 个元素调用 aMethod

那你为什么不这样称呼它呢:

var onlyOneItem = aMethod(original.ElementAt(898));

如果你想得到几个特定的​​元素,只是不想让 LINQ 一直重新计算 aMethod,那么把你的结果变成一个 List 或数组:

var listWithADifficultSelect = original.Select(aMethod).ToList(); // or ToArray();

因此 Select 及其所有 aMethod 调用只执行一次,您可以访问所有元素而无需重新调用 aMethod


如果您想编写自己的 LINQ 方法来完成比 LINQ 已经做的更多的事情,您可以轻松实现自己的扩展:

public static class MyLinq
{
    public static IEnumerable<TResult> MySelect<TSource,TResult>(this IEnumerable<TSource>, Func<TSource,TResult> selector)
    {
        // implement yourself
    }
    public static TSource MyElementAt<TSource>(this IEnumerable<TSource>, int index)
    {
        // implement yourself
    }
}

甚至可以很好地转换为 SQL(如果这是一个问题)的通用解决方案是使用 SkipTake。您可以跳过前 n-1 项,然后从原始 IEnumerable(或 IQueryable)中取出 1

var original = Enumerable.Range(0, 1000);
var onlyOneItem = original.Skip(898 - 1).Take(1).Select(aMethod);

SkipTake 是 Linq 等价于 SQL 的 OFFSETLIMIT.

在像您的示例这样的简化情况下,您不会看到任何性能改进,但如果您在实际应用程序中有一个昂贵的查询,这样您就可以避免获取任何不必要的元素