【发布时间】:2013-07-05 16:12:12
【问题描述】:
我想做的事,简短版:
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 两次以获取其计数,但我也想尽可能避免创建不必要的副本。
这是我要创建的函数的骨架
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
}
【问题讨论】:
-
不幸的是,LINQ 的设计方式使这完全不可能。
-
@SLaks:通常“完全不可能”被证明是可能的。我认为您可以使用表达式树来做到这一点,但这超出了我的能力范围。
-
您能否创建自己的包装类来保存原始源代码,并在需要时应用任何排序?
-
为什么不先数数再排序?
-
我想创建一个库函数,无需深入了解其内部工作原理即可调用。当然,我可以让调用者将计数作为附加参数传递给 DoSomething(),或者要求源是一些 IMyCustomOrderedEnumerable,但两者看起来都很麻烦和不优雅。
标签: c# performance linq reflection ienumerable