Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does IEnumerable.ToList produce new objects

Tags:

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)); 

If I modify the copied object the original does not change.

I understand from this that System.Linq.IEnumerable<T> collections will always produce new objects even though they are reference types when calling ToList()/ToArray()? Am I missing something as other SO questions are insistent only references are copied.

like image 759
Jaycee Avatar asked Feb 20 '14 22:02

Jaycee


2 Answers

Actually, the reason this is false is because your enumerable actually gets enumerated twice, once in the call originalEnumerable.ToList(), and a second time when you invoke originalEnumerable.ElementAt(0).

Since it enumerates twice, it ends up creating new objects each time it is enumerated.

like image 170
Pete Avatar answered Sep 24 '22 11:09

Pete


You're missing the fact, that Enumerable.Range is lazy, so this line actually produce nothing but in-memory query definition:

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

Calling ToList() fires the query and creates list with new items:

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

Calling originalEnumerable.ElementAt(0) here

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

fires the query again, and produces yet another new items.

Update

So you should say, that Enumerable.Range produces new items, not ToList().

If your source collection would be already evaluated (e.g. TestClass[] array) ToList() won't create new items:

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]); 
like image 38
MarcinJuraszek Avatar answered Sep 25 '22 11:09

MarcinJuraszek