链式LINQ语句有多高效?

22 浏览
0 Comments

链式LINQ语句有多高效?

我有一个案例,我想迭代集合中除最后两个元素之外的所有元素。\n假设我用一种奇怪的方式来做,比如 x.Reverse().Skip(2).Reverse()。\n每个LINQ操作是否会生成一个嵌套的迭代器或引起枚举等,还是它比这更聪明?在这种情况下,在底层会发生什么?

0
0 Comments

链式LINQ语句的效率问题是由下面的情况引起的:当我们开始枚举序列时(即使用非延迟方法,即不返回IEnumerable的LINQ方法,或使用foreach循环),第一个Reverse方法会将整个序列枚举一次,并将其存储在一个缓冲区中,然后从最后一个元素开始反向迭代。Skip(2)方法只会枚举2个元素。第二个Reverse方法会创建一个新的缓冲区,其中包含这两个元素,并从原始序列的正向顺序开始反向迭代。也就是说,原始序列的元素会被放入一个缓冲区中,最后两个元素会被放入第二个缓冲区中。第二个缓冲区会先迭代倒数第二个元素,然后是最后一个元素。因此,每个元素都被迭代一次,最后两个元素又被再次迭代一次。如果迭代是一项耗费大量工作的操作,可以考虑创建一个List,然后取出最后两个元素。这样只需迭代元素一次。

解决这个问题的方法是查看LINQ语句的源代码。如果你想了解其他LINQ语句的实现方式,只需查看源代码即可。

0
0 Comments

链式LINQ语句在编程中被广泛使用,因为它可以简化代码并提高代码的可读性。然而,有人可能会对链式LINQ语句的效率产生疑问。这个问题的出现可能是因为有人对链式LINQ语句的实现方式感到好奇,并想了解它的效率如何。

对于这个问题,一种解决方法是查看LINQ操作的源代码。在提到的内容中,某些情况下了使用Count()方法时的优化。源代码中可以看到,Count方法会根据检测到的底层类型进行一些优化,从而避免完全遍历整个序列。这个源代码可以作为参考,帮助我们理解链式LINQ语句的实现方式。

然而,对于完全理解LINQ的工作原理来说,查看源代码可能并不是最好的方法。有人建议查看Enumerable的参考源代码,因为大部分LINQ的实现都在这里。但是,完全解释LINQ的工作原理超出了这个网站的范围。

总之,链式LINQ语句的效率取决于具体的操作和数据量。如果想要深入了解链式LINQ语句的工作原理,可以查看源代码作为参考。但是,完全理解LINQ的实现可能需要更深入的学习和了解。

0
0 Comments

链式LINQ语句的效率如何?这个问题的出现是因为在LINQ查询中使用多个操作符链式调用会导致多次迭代,从而影响查询的效率。为了解决这个问题,可以考虑使用其他方式来优化查询,例如使用索引或直接操作集合。

具体来说,当使用Reverse、Skip和Take操作符时,查询会进行多次迭代,这可能会导致性能下降。如果源集合是ICollection类型,那么Reverse操作会使用CopyTo方法将数据一次性复制到缓冲数组中,以实现快速反转。如果源集合不是ICollection类型,那么Reverse操作会先将数据迭代到缓冲数组中,然后再进行反转操作。而Skip操作会跳过前面的元素,只返回后面的元素。

为了验证查询在材料化之前不会进行迭代,可以通过以下示例进行验证:

void Main()
{
    var query = MyValues().Reverse().Skip(2).Reverse();
    Console.WriteLine($"After query before materialization");
    var results = query.ToList();
    Console.WriteLine(string.Join(",", results));
}
public IEnumerable MyValues()
{
    for(int i = 0; i < 10; i ++)
    {
        Console.WriteLine($"yielding {i}");
        yield return i;
    }
}

运行以上代码会输出以下结果:

After query before materialization
yielding 0
yielding 1
yielding 2
yielding 3
yielding 4
yielding 5
yielding 6
yielding 7
yielding 8
yielding 9
0,1,2,3,4,5,6,7

从结果可以看出,查询在材料化之前并没有进行迭代。

相比之下,如果使用x.Take(x.Count() - 2)的方式,查询会在材料化之前进行两次迭代,一次用于Count操作,一次用于ToList操作。以下是使用不同代码的示例及其输出结果:

void Main()
{
    var x = MyValues();
    var query = x.Take(x.Count() - 2);
    Console.WriteLine($"After query before materialization");
    var results = query.ToList();
    Console.WriteLine(string.Join(",", results));
}
public IEnumerable MyValues()
{
    for(int i = 0; i < 10; i ++)
    {
        Console.WriteLine($"yielding {i}");
        yield return i;
    }
}

运行以上代码会输出以下结果:

yielding 0
yielding 1
yielding 2
yielding 3
yielding 4
yielding 5
yielding 6
yielding 7
yielding 8
yielding 9
After query before materialization
yielding 0
yielding 1
yielding 2
yielding 3
yielding 4
yielding 5
yielding 6
yielding 7
0,1,2,3,4,5,6,7

从结果可以看出,使用Take和Count操作的方式会在材料化之前进行两次迭代。

因此,哪一种方式更好完全取决于源集合的类型。对于ICollection或ICollection类型的集合,使用Take和Count操作会更好(除非在创建查询和材料化查询之间源集合可能发生变化),但如果源集合既不是ICollection也不是ICollection类型,可能更倾向于使用双重Reverse操作来避免对源集合进行两次迭代(特别是如果在创建查询和材料化查询之间源集合可能发生变化的情况下),但这必须权衡总迭代次数和内存使用量的增加。

0