2012-07-10 22 views
10

Với ngạc nhiên tuyệt vời, tôi đã quan sát các hành vi sau đây hôm nay: Cho một lớpThuộc tính đang thay đổi của IEnumerator <T> .Current

class Foo 
{ 
    prop int FooNumber { get; set; } 
} 

và mã này

IEnumerable<Foo> foos = Enumerable.Range(0,3).Select(new Foo()); 

foreach (var foo in foos) 
    foo.Bar = 5; 

foreach (var foo in foos) 
    Console.Write(foo.Bar); // Writes 000 

khi khởi tạo foos-new List<Foo>{ new Foo(), new Foo(), new Foo() } làm cho vòng ghi " 555 ".

Câu hỏi của tôi: Tại sao điều này xảy ra và có cách nào để phá vỡ whithout này bằng cách sử dụng .ToList() (cần nhận xét vì có vẻ như không cần thiết ở đây).

+4

Chào mừng bạn đến với thế giới tuyệt vời của những gì ReSharper gọi là "có thể liệt kê nhiều". Một liệt kê là ** không ** một bộ sưu tập. Thực tế là đôi khi một enumerable là hơn một bộ sưu tập và bạn có thể sửa đổi nó bên dưới là một tác dụng phụ. –

Trả lời

20

Điều này xảy ra vì foos được tạo động mỗi khi bạn liệt kê. Vì vậy, trong lần lặp đầu tiên bạn đang thiết lập các giá trị thuộc tính trên các đối tượng không còn được tham chiếu bởi bất cứ điều gì sau khi lặp lại kết thúc. Lặp lại thứ hai hoạt động trên các đối tượng mới được xây dựng có giá trị thuộc tính mặc định.

Khởi foos vào một danh sách các đối tượng "dai dẳng" thay đổi mọi thứ, cũng như sử dụng .ToList() với cùng lý do (một danh sách "cố định" được xây dựng và lặp hơn hai lần; bản gốc tự động sản xuất IEnumerable chỉ là lặp qua một lần).

Đã xác định rằng bạn nên sử dụng .ToList() đây: nói chung tôi không cảm thấy rằng nó cần bình luận vì nó không phải là phong tục để lặp qua chuỗi động sản xuất nhiều hơn một lần (tôi tin rằng rất nhiều mã công cụ phân tích cảnh báo việc này), nhưng bằng mọi cách, hãy viết một.

+0

Điều đó giải thích, cảm ơn. Tôi đã bị ấn tượng sai lầm, rằng sau khi điều tra đầu tiên, kết quả sẽ được lưu trữ ở đâu đó. – Jens

3

Có vẻ như điều hiển nhiên xảy ra: mỗi khi bạn liệt kê, bạn đang khởi tạo các đối tượng Foo mới.

Nếu bạn muốn giá trị tài sản (Foo.Bar) được giữ nguyên, thì bạn sẽ phải giữ số của Foo ở đâu đó và ToList() là cách đơn giản để thực hiện việc này.

Các vấn đề liên quan