我最近了解到.NET的LINQ实现创建的对象对于特定的枚举类型效率低下.
看看这段代码:
public class DummyCollection : ICollection<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new Exception();
}
public int Count
{
get
{
return 10;
}
}
//some more interface methods
}
Run Code Online (Sandbox Code Playgroud)
基本上,DummyCollection的实例大小为10,但如果实际枚举则抛出异常.
现在这里:
var d = new DummyCollection();
Console.WriteLine(d.Count());
Run Code Online (Sandbox Code Playgroud)
打印10没有错误,但这段代码:
var l = d.Select(a=> a);
Console.WriteLine(l.Count());
Run Code Online (Sandbox Code Playgroud)
抛出异常,尽管说我的尺寸也是10也是微不足道的(因为Select提供了1对1的映射).这基本上意味着,当检查Ienumerable的长度时,输入可能是Select-wrapped Collection,从而将计算时间从O(1)扩展到惊人的O(n)(可能更糟,如果选择功能特别麻烦).
我知道当你要求LINQ的泛型时你会牺牲效率,但这似乎是一个很难解决的问题.我在网上查了一下,找不到任何人解决这个问题.有没有办法绕过这个缺点?有人在调查这个吗?有人修这个吗?这只是一个边缘案例,这不是一个问题吗?任何见解都表示赞赏.
您可以在此处查看Count()扩展方法的实现方式.基本上是这样的:
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)
正如您所看到的,方法检查首先是source类型,ICollection<TSource>或者ICollection,如果是这种情况,那么就不需要迭代计算元素,只返回Count属性.
在第一种情况下,Count属性称为返回,10并且GetEnumerator()永远不会调用方法.
当您使用Select()方法时,您将集合包装到另一个不是的类型中ICollection(在上面的链接中您也可以看到Select()实现),因此迭代是必要的.
在第二个情况下,当你调用Count()你的GetEnumerator()方法被调用,并抛出异常.
| 归档时间: |
|
| 查看次数: |
150 次 |
| 最近记录: |