我想做的是,简短的版本:
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 ()方法自动执行此操作。
函数myEnumerable.OrderBy()返回一个IOrderedEnumerable。显然不能将IOrderedEnumerable转换为ICollection,因此调用Count()将消耗整个过程。
但是排序不会改变元素的数量,IOrderedEnumerable必须保持对其源的引用。因此,如果该源是ICollection,则应该可以在不使用它的情况下从IOrderedEnumerable中确定计数。
我的目标是有一个库方法,它接受一个带有n个元素的IEnumerable,然后在n/2位置检索元素;
我希望避免在IEnumerable上重复两次以获得它的计数,但如果可能的话,我也希望避免创建不必要的副本。
下面是我想要创建的函数的框架
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
}发布于 2013-07-05 16:41:33
让我们想想这段代码到底是什么样子的:
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();
}
}用法:
var source = new[]{ 2, 4, 6, 1, 9 }.OrderBy(x => x);
int count = source.Count();发布于 2013-07-05 16:38:13
如果您想要创建一个性能很好的解决方案,我会考虑创建一个包含集合或IOrderedEnumerable等的重载。所有的“是”和“作为”的排版和铸造不可能是好的东西,你正在创造。
你在重塑方向盘。linq的"Count()“函数可以做您想做的事情。
另外,添加this关键字并将其添加到一个漂亮的扩展方法中,以满足自己和其他人使用代码的需要。
DoSomething(this Collection source);
DoSomething<T>(this List<T> source);
DoSomething<T>(this IOrderedEnumerable<T> source);等等。
发布于 2013-07-05 17:43:01
另一种方法是实现实现IOrderedEnumerable<T>的类。然后,您可以实现类成员,这些类成员将中断通常的Linq扩展方法,并提供一个查看原始枚举的计数方法。
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();
}
}https://stackoverflow.com/questions/17493076
复制相似问题