我只是想知道在C#中.ToList()
上调用IEnumerable
时会发生什么。这些项目是否实际上被复制到堆上的全新重复项目,或者新列表是否只是引用堆上的原始项目?
我想知道,因为有人告诉我调用ToList很昂贵,而如果只是将现有对象分配到新列表,那就是轻量级调用。
我写过这个小提琴https://dotnetfiddle.net/s7xIc2 只是检查哈希码足以知道吗?
答案 0 :(得分:8)
IEnumerable
不必包含任何内容列表。它可以(并且经常会)在请求时解析每个当前项。
另一方面,IList
是所有项目的完整内存副本。
所以答案是......这取决于。 什么是支持你的IEnumerable?如果它的文件系统然后是,则调用.ToList可能非常昂贵。如果它已经是一个内存列表,那么不,调用.ToList不会非常昂贵。
作为一个例子,假设您创建了一个IEnumerable,每次生成并返回一个随机数.Next被调用。在这种情况下,调用IEnumerable上的.ToList永远不会返回,并最终会抛出Out Of Memory异常。
然而,IEnumerable的数据库对象具有有限的边界(通常是:)),只要所有数据都适合内存,调用.ToList就完全合适了。
答案 1 :(得分:3)
以下是ToList的一个版本:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
return new List<TSource>(source);
}
它从源代码创建一个新列表,这里是构造函数:
// Constructs a List, copying the contents of the given collection. The
// size and capacity of the new list will both be equal to the size of the
// given collection.
//
public List(IEnumerable<T> collection) {
if (collection==null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
Contract.EndContractBlock();
ICollection<T> c = collection as ICollection<T>;
if( c != null) {
int count = c.Count;
if (count == 0)
{
_items = _emptyArray;
}
else {
_items = new T[count];
c.CopyTo(_items, 0);
_size = count;
}
}
else {
_size = 0;
_items = _emptyArray;
// This enumerable could be empty. Let Add allocate a new array, if needed.
// Note it will also go to _defaultCapacity first, not 1, then 2, etc.
using(IEnumerator<T> en = collection.GetEnumerator()) {
while(en.MoveNext()) {
Add(en.Current);
}
}
}
}
复制项目。
答案 2 :(得分:1)
ToList()
创建一个新的List对象,该对象将包含对原始对象的引用或对象的副本struct
。
例如,int的列表将是完整副本。 “产品”列表仅指产品,而非完整副本。如果修改了原件,则也会修改列表中的产品。