2014-02-20 31 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() }); 

調用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]); 
11

其實,這是錯誤的原因是因爲你的枚舉實際上得到列舉兩次,在一次通話originalEnumerable.ToList(),第二時間,當你調用originalEnumerable.ElementAt(0)

由於它枚舉了兩次,它每次枚舉時都會創建新對象。

+2

如果你已經安裝了ReSharper的,它甚至會強調方法枚舉IEnumerable和告訴你關於這個,這非常方便。在討厭的情況下,多次(不必要的)枚舉可能花費很多時間。 –

相關問題