Bid*_*dou 21 c# linq performance ienumerable skip-take
我刚看了.NET Framework 的Skip
/ Take
extension方法的源代码(在IEnumerable<T>
类型上),发现内部实现正在使用该GetEnumerator
方法:
// .NET framework
public static IEnumerable<TSource> Skip<TSource>(this IEnumerable<TSource> source, int count)
{
if (source == null) throw Error.ArgumentNull("source");
return SkipIterator<TSource>(source, count);
}
static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count)
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (count > 0 && e.MoveNext()) count--;
if (count <= 0)
{
while (e.MoveNext()) yield return e.Current;
}
}
}
Run Code Online (Sandbox Code Playgroud)
假设我有IEnumerable<T>
1000个元素(底层类型是List<T>
).如果我正在做list.Skip(990)会怎么样.拿(10)?在进入最后十个元素之前,它是否会通过990首元素进行迭代?(这就是我的理解).如果是,那么我不明白为什么微软没有实现这样的Skip
方法:
// Not tested... just to show the idea
public static IEnumerable<T> Skip<T>(this IEnumerable<T> source, int count)
{
if (source is IList<T>)
{
IList<T> list = (IList<T>)source;
for (int i = count; i < list.Count; i++)
{
yield return list[i];
}
}
else if (source is IList)
{
IList list = (IList)source;
for (int i = count; i < list.Count; i++)
{
yield return (T)list[i];
}
}
else
{
// .NET framework
using (IEnumerator<T> e = source.GetEnumerator())
{
while (count > 0 && e.MoveNext()) count--;
if (count <= 0)
{
while (e.MoveNext()) yield return e.Current;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
事实上,他们为Count
方法做了这样的例子......
// .NET Framework...
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
checked
{
while (e.MoveNext()) count++;
}
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
那是什么原因?
Sve*_*sen 14
在Jon Skeet重新实施Linq的优秀教程中,他(简要地)讨论了这个问题:
虽然大多数这些操作都无法进行合理优化,但在源实现IList时优化Skip是有意义的.我们可以跳过跳过,可以这么说,直接找到适当的索引.这不会发现在迭代之间修改源的情况,这可能是我所知道的在框架中没有实现的一个原因.
这似乎是推迟优化的合理理由,但我同意,对于特定情况,如果您可以保证您的源不能/不会被修改,那么进行优化可能是值得的.