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

Jay*_*cee 5 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)); 
Run Code Online (Sandbox Code Playgroud)

如果我修改复制的对象,原始文件不会更改.

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

Pet*_*ete 11

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

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

  • 如果你安装了ReSharper,它甚至会强调枚举IEnumerable的方法并告诉你这个,这非常方便.在令人讨厌的场景中,多个(不必要的)枚举可能会花费大量时间. (2认同)

Mar*_*zek 7

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

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

调用ToList()触发查询并使用新项创建列表:

List<TestClass> listFromEnumerable = originalEnumerable.ToList();
Run Code Online (Sandbox Code Playgroud)

originalEnumerable.ElementAt(0)这里打电话

bool test = ReferenceEquals(listFromEnumerable[0], originalEnumerable.ElementAt(0)); 
Run Code Online (Sandbox Code Playgroud)

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

更新

所以你应该说,这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]); 
Run Code Online (Sandbox Code Playgroud)