为什么IEnumerable.ToList会生成新对象

时间:2014-02-20 22:16:12

标签: c# linq

public class TestClass
{
   public int Num { get; set; }
   public string Name { get; set; }
}

IEnumerable<TestClass> originalEnumerable
   = Enumerable.Range(1, 1).
                Select(x => new TestClass() { Num = x, Name = x.ToString() });

List<TestClass> listFromEnumerable = originalEnumerable.ToList();

// false
bool test = ReferenceEquals(listFromEnumerable[0], originalEnumerable.ElementAt(0)); 

如果我修改复制的对象,原件不会改变。

我从中理解,System.Linq.IEnumerable<T>集合总是会产生新对象,即使它们在调用ToList() / ToArray()时是引用类型?我错过了一些东西,因为其他SO问题坚持只有复制参考。

2 个答案:

答案 0 :(得分:11)

实际上,这是错误的原因是因为你的枚举实际上被枚举了两次,一次是在调用originalEnumerable.ToList()中,第二次是在你调用originalEnumerable.ElementAt(0)时。< / p>

由于它枚举了两次,因此每次枚举时都会创建新对象。

答案 1 :(得分:7)

你错过了Enumerable.Range是懒惰的事实,所以这一行实际上只产生了内存中的查询定义:

IEnumerable<TestClass> originalEnumerable
   = Enumerable.Range(1, 1).
                Select(x => new TestClass() { Num = x, Name = x.ToString() });

调用ToList()会触发查询并创建包含新项目的列表:

List<TestClass> listFromEnumerable = originalEnumerable.ToList();

在此处拨打originalEnumerable.ElementAt(0)

bool test = ReferenceEquals(listFromEnumerable[0], originalEnumerable.ElementAt(0)); 

再次触发查询,并生成另一个新项目。

更新

所以你应该说,Enumerable.Range产生新项目,而不是ToList()

如果您的源集合已经过评估(例如TestClass[]数组)ToList()将无法创建新项:

IEnumerable<TestClass> originalEnumerable
   = Enumerable.Range(1, 1).
                Select(x => new TestClass() { Num = x, Name = x.ToString() })
                .ToArray();

List<TestClass> listFromEnumerable = originalEnumerable.ToList();

// true
bool test = ReferenceEquals(listFromEnumerable[0], originalEnumerable[0]);