计算 IOrderedEnumerable 的数量而不消耗它

2024-05-01

我想做的,简短版本:

var source = new[]{2,4,6,1,9}.OrderBy(x=>x);
int count = source.Count; // <-- get the number of elements without performing the sort

长版:

确定一个元素的数量IE可枚举,有必要迭代所有元素。这可能是一项非常昂贵的操作。

If the IE可枚举可以投射到收藏,则无需迭代即可快速确定计数。 LINQ Count() 方法自动执行此操作。

功能Enumerable.OrderBy()返回一个有序枚举. An 有序枚举显然不能投射到收藏,所以调用Count()将消耗整个东西。

但排序不会改变元素的数量,并且有序枚举必须保留对其来源的引用。因此,如果该来源是收藏,应该可以确定计数有序枚举而不消耗它。

我的目标是拥有一个库方法,需要一个IE可枚举n 个元素,然后例如检索位置 n/2 处的元素;

我想避免迭代IE可枚举两次只是为了得到它的计数,但我也想尽可能避免创建不必要的副本。


这是我要创建的函数的骨架

public void DoSomething(IEnumerable<T> source)
{
    int count; // What we do with the source depends on its length

    if (source is ICollection)
    {
        count = source.Count(); // Great, we can use ICollection.Count
    }
    else if (source is IOrderedEnumerable)
    {
        // TODO: Find out whether this is based on an ICollection, 
        // TODO: then determine the count of that ICollection
    }
    else
    {
        // Iterating over the source may be expensive, 
        // to avoid iterating twice, make a copy of the source
        source = source.ToList();
        count = source.Count();
    }

    // do some stuff

}

让我们想想这段代码实际上是什么样子的:

var source = new[]{ 2, 4, 6, 1, 9 }.OrderBy(x => x);
int count = source.Count();

它与

int count = Enumerable.Count(Enumerable.OrderBy(new[]{ 2, 4, 6, 1, 9 }, x => x));

的结果Enumerable.OrderBy(new[]{ 2, 4, 6, 1, 9 }, x => x)被传递到Count扩大。你无法避免OrderBy执行。因此它是非流运算符,它在返回某些内容之前消耗所有源,这些内容将被传递给Count.

因此,避免迭代所有集合的唯一方法是避免OrderBy- 排序前对物品进行计数。


更新:您可以在任何OrderedEnumerable- 它将使用反射来获取source现场OrderedEnumerable<T>它保存源序列。然后检查这个序列是否是集合,并使用Count不执行命令:

public static class Extensions
{
    public static int Count<T>(this IOrderedEnumerable<T> ordered)
    {
        // you can check if ordered is of type OrderedEnumerable<T>
        Type type = ordered.GetType();
        var flags = BindingFlags.NonPublic | BindingFlags.Instance;
        var field = type.GetField("source", flags);
        var source = field.GetValue(ordered);
        if (source is ICollection<T>)
            return ((ICollection<T>)source).Count;

        return ordered.Count();
    }
}

Usage:

var source = new[]{ 2, 4, 6, 1, 9 }.OrderBy(x => x);
int count = source.Count();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

计算 IOrderedEnumerable 的数量而不消耗它 的相关文章

随机推荐