C# 是否将IEnumerable转换为IList<;TResult>;列举序列,为什么会优先考虑这一点?
在中的某个反编译源代码中,我在快速验证检查后定义的C# 是否将IEnumerable转换为IList<;TResult>;列举序列,为什么会优先考虑这一点?,c#,.net,linq,ienumerable,C#,.net,Linq,Ienumerable,在中的某个反编译源代码中,我在快速验证检查后定义的FirstOrDefault()的正文中发现了这个有趣的片段: IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) return list[0]; } else { using (IEnumerator<TSource&g
FirstOrDefault()
的正文中发现了这个有趣的片段:
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0)
return list[0];
}
else
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
return enumerator.Current;
}
}
IList list=源代码为IList;
如果(列表!=null)
{
如果(list.Count>0)
返回列表[0];
}
其他的
{
使用(IEnumerator enumerator=source.GetEnumerator())
{
if(枚举数.MoveNext())
返回枚举数.Current;
}
}
我发现这有两个奇怪之处
首先,这会列举来源吗?显然,如果为IList定义了.Count
属性,那么如果底层序列本身恰好是惰性的,该怎么办
第二,如果它确实枚举了源代码,为什么这是一个选择的实现?如果只使用下面的代码(
if()
check),这不是更有意义吗?不,它不枚举任何内容。它只是检查source
中的对象实例是否实现了IList
,并使用该接口
这个接口的要点很可能是优化,因为直接访问元素比使用枚举器要好
如果您想了解更多关于LINQ是如何实现的,请查看Jon Skeet的。您完全正确,理论上,“惰性”序列可以实现
IList
,从而导致计数
属性访问不必要地枚举整个序列,只会丢弃生成的项。在这种情况下,FirstOrDefault选择的“优化”实际上会让事情变得更糟
但在实践中,通常只希望能够通过索引快速访问项的集合序列实现IList
。这就是为什么,例如,LinkedList
没有实现此接口,即使它可以提供它所需的所有功能
也就是说,这个FirstOrDefault优化对于列表来说并不是一个很大的性能胜利。充其量,它会阻止在堆上分配枚举器对象。不进行这种优化不会是世界末日
首先,这会列举来源吗
不,不会的。如果此基础集合是一个IList
,这意味着它始终保留一个反映集合大小的更新的Count
属性,这是一个O(1)
操作。不需要列举。我没有遇到实现上述接口的惰性集合,因为这会违反用户的假设,即Count
属性是一个无枚举操作
第二,如果它确实枚举了源代码,那么为什么选择这个实现呢
这无关紧要,因为没有执行任何枚举,只是一个简单的强制转换。是否有任何东西记录了
IList.Item(0)
必须返回枚举器的第一项的承诺?由于集合缺少任何其他接口,其项可以通过整数索引读取和写入,因此稀疏数组类型可能具有只生成已写入项的枚举数似乎并不令人难以置信。也许这不是一个伟大的设计,但它将允许这样一种类型以一些明智的方式使用,否则是不可能的。