算一笔IOrderedEnumerable不消耗它 [英] Count an IOrderedEnumerable without consuming it

查看:159
本文介绍了算一笔IOrderedEnumerable不消耗它的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要做的,短版什么:

 无功源=新[] {2,4,6,1 ,9} .OrderBy(X =&X的催化剂); 
诠释计数= source.Count; //&下; - 得到的元素数不进行排序






龙版本:



要确定的元素数量的的IEnumerable 的,这里有必要遍历所有元素。这可能是一个非常昂贵的操作。



如果在的IEnumerable 的可以转换的的ICollection 的,则计数可以在不迭代很快确定。该LINQ计数()方法会自动完成。



函数的 myEnumerable.OrderBy()的返回的 IOrderedEnumerable 的。一个的 IOrderedEnumerable 的显然不能被强制转换的的ICollection 的,因此调用的计数()的会消耗整个事情。



但是排序不改变元件的数目,以及一个 IOrderedEnumerable 的具有保持其来源的引用。因此,如果该源是 ICollection的的,它应该是能确定从 IOrderedEnumerable 的计数不消耗它



我的目标是有一个库方法,它接受一个的的IEnumerable 的有n个元素,然后例如位置n / 2检索元素;



我要避免通过的的IEnumerable 的两次即可获取其数,但我也想避免,如果在所有可能制造不必要的副本。


$迭代b $ b


下面是我要创建的函数的骨架

 公共无效DoSomething的(IEnumerable的< T>源)
{
诠释计数; //我们做的是什么来源取决于其长度

如果(源的ICollection)
{
计数= source.Count(); //太好了,我们可以用ICollection.Count
}
,否则如果(来源IOrderedEnumerable)
{
// TODO:找出这是否是基于ICollection的,
// TODO:然后确定ICollection的
的数量}
,否则
{
//遍历源可能是昂贵的,
//至避免迭代两次,使源
源= source.ToList副本();
计数= source.Count();
}

//做一些东西

}


< DIV CLASS =h2_lin>解决方案

让我们觉得这段代码实际上是这样的:

  VAR源=新[] {2,4,6,1,9} .OrderBy(X =&X的催化剂); 
诠释计数= source.Count();



这是一样的。



 诠释计数= Enumerable.Count(Enumerable.OrderBy(新[] {2,4,6,1,9},X => X)); 



的结果Enumerable.OrderBy(新[] {2,4,6 ,1,9},X => X)传递到计数扩展。你无法避免排序依据执行。因此它是不流运营商,它消耗返回的东西之前的所有来源,这将被传递给计数



所以,只有这样才能避免循环访问所有的收藏,是避免排序依据 - 排序前数项






更新:您可以拨打任何本扩展方法 OrderedEnumerable - 它会使用反射来获取 OrderedEnumerable<领域; T> 持有源序列。然后检查这个序列是收集,并使用计数而不执行顺序:

 公共静态类扩展
{
公共静态诠释计数< T>(这IOrderedEnumerable< T>订购)
{
//你可以检查是否有序的类型是OrderedEnumerable<的; T> $ B $型B型= ordered.GetType();
VAR标志= BindingFlags.NonPublic可| BindingFlags.Instance;
VAR字段= type.GetField(源,标志);
无功源= field.GetValue(有序);
如果(源的ICollection< T>)
回报率((ICollection的< T>)源).Count之间;

返回ordered.Count();
}
}



用法:

 无功源=新[] {2,4,6,1,9} .OrderBy(X => X); 
诠释计数= source.Count();


What I want to do, short version:

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


Long version:

To determine the number of elements in an IEnumerable, it is neccessary to iterate over all elements. This could potentially be a very expensive operation.

If the IEnumerable can be cast to ICollection, then the count can be determined quickly without iterating. The LINQ Count() method does this automatically.

The function myEnumerable.OrderBy() returns an IOrderedEnumerable. An IOrderedEnumerable can obviously not be cast to ICollection, so calling Count() will consume the whole thing.

But sorting does not change the number of elements, and an IOrderedEnumerable has to keep a reference to its source. So if that source is an ICollection, it should be possible to determine the count from the IOrderedEnumerable without consuming it.

My goal is to have a library method, that takes an IEnumerable with n elements, and then for example retrieves the element at position n/2;

I want to avoid iterating over the IEnumerable twice just to get its count, but I also want to avoid creating an unnecessary copy if at all possible.


Here is a skeleton of the function I want to create

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

}

解决方案

Let's think what this code actually looks like:

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

It is same as

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

Result of Enumerable.OrderBy(new[]{ 2, 4, 6, 1, 9 }, x => x) is passed into Count extension. You cannot avoid OrderBy execution. And thus it is non-streaming operator, it consumes all source before returning something, which will be passed to Count.

So, the only way to avoid iterating over all collection, is avoiding OrderBy - count items before sorting.


UPDATE: You can call this extension method on any OrderedEnumerable - it will use reflection to get source field of OrderedEnumerable<T> which holds source sequence. Then check if this sequence is collection, and use Count without executing ordering:

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();

这篇关于算一笔IOrderedEnumerable不消耗它的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆