6

私がやりたいこと、短いバージョン:

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

長いバージョン:

IEnumerableの要素数を決定するには、すべての要素を反復処理する必要があります。これは、非常にコストのかかる操作になる可能性があります。

IEnumerableをICollectionにキャストできる場合は、反復せずにカウントをすばやく決定できます。LINQ Count() メソッドはこれを自動的に行います。

関数myEnumerable.OrderBy()はIOrderedEnumerableを返します。IOrderedEnumerableは明らかにICollectionにキャストできないため、 Count()を呼び出すとすべてが消費されます。

ただし、並べ替えによって要素の数は変更されず、IOrderedEnumerableはそのソースへの参照を保持する必要があります。したがって、そのソースがICollectionの場合、 IOrderedEnumerableを消費せずにカウントを決定できるはずです。

私の目標は、n 個の要素を持つIEnumerableを取得し、たとえば n/2 の位置にある要素を取得するライブラリ メソッドを作成することです。

カウントを取得するためだけにIEnumerableを 2 回繰り返すことは避けたいのですが、可能であれば不要なコピーを作成することも避けたいと考えています。


これが私が作成したい関数のスケルトンです

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

}
4

3 に答える 3

0

別のアプローチは、 を実装するクラスを実装することIOrderedEnumerable<T>です。その後、通常の Linq 拡張メソッドを短縮するクラス メンバーを実装し、元の列挙を調べる count メソッドを提供できます。

public class MyOrderedEnumerable<T> : IOrderedEnumerable<T>
{
    private IEnumerable<T> Original;
    private IOrderedEnumerable<T> Sorted;

    public MyOrderedEnumerable(IEnumerable<T> orig)
    {
            Original = orig;
            Sorted = null;
    }

    private void ApplyOrder<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
    {
            var before = Sorted != null ? Sorted : Original;
            if (descending)
                    Sorted = before.OrderByDescending(keySelector, comparer);
            else
                    Sorted = before.OrderBy(keySelector, comparer);
    }

    #region Interface Implementations

    public IEnumerator<T> GetEnumerator()
    {
            return Sorted != null ? Sorted.GetEnumerator() : Original.GetEnumerator();
    }

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

    public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(
            Func<T, TKey> keySelector,
            IComparer<TKey> comparer,
            bool descending)
    {
            var newSorted = new MyOrderedEnumerable<T>(Original);
            newSorted.ApplyOrder(keySelector, comparer, descending);
            return newSorted;
    }

    #endregion Interface Implementations


    //Ensure that OrderBy returns the right type. 
    //There are other variants of OrderBy extension methods you'll have to short-circuit
    public MyOrderedEnumerable<T> OrderBy<TKey>(Func<T, TKey> keySelector)
    {   
            Console.WriteLine("Ordering");
            var newSorted = new MyOrderedEnumerable<T>(Original);
            newSorted.Sorted = (Sorted != null ? Sorted : Original).OrderBy(keySelector);
            return newSorted;
    }

    public int Count()
    {
            Console.WriteLine("Fast counting..");
            var collection = Original as ICollection;
            return collection == null ? Original.Count() : collection.Count;
    }

    public static void Test()
    {
            var nums = new MyOrderedEnumerable<int>(Enumerable.Range(0,10).ToList());
            var nums2 = nums.OrderBy(x => -x);
            var z = nums.Count() + nums2.Count();
    }
}
于 2013-07-05T17:43:01.880 に答える