2014-02-20 3 views
5
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)); 

Если я модифицирую скопированный объект, оригинал не изменяется.Почему IEnumerable.ToList создает новые объекты

Я понимаю из этого, что коллекции System.Linq.IEnumerable<T> всегда будут создавать новые объекты, даже если они являются ссылочными типами при вызове ToList()/ToArray()? Я что-то упускаю, потому что другие SO-вопросы настойчивы, только копии копируются.

ответ

7

Вы упускаете тот факт, что Enumerable.Range ленив, поэтому эта линия на самом деле ничего не производят, но в памяти определение запроса:

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

Calling ToList() пожаров запрос и создает список новых элементов:

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

Вызов originalEnumerable.ElementAt(0) здесь

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

снова запускает запрос и производит еще одно новшество.

Update

Таким образом, вы должны сказать, что 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]); 
11

На самом деле, причина это ложь, потому что ваш перечислимы фактически получает перечисленные дважды, один раз в вызове originalEnumerable.ToList(), и второй раз, когда вы вызываете originalEnumerable.ElementAt(0).

Поскольку он перечисляет дважды, он заканчивает создание новых объектов каждый раз, когда он перечисляется.

+2

И если вы установили ReSharper, это будет даже подчеркнут методы перечисления IEnumerable и сказать вам, об этом, что очень удобно. В неприятных сценариях многократное (ненужное) перечисление может стоить много времени. –

Смежные вопросы