Mã ví dụ của Resharper để giải thích "Có thể liệt kê nhiều IEnumerable"


76

Đôi khi Resharper cảnh báo về:

Có thể liệt kê nhiều IEnumerable

một câu hỏi SO về cách xử lý vấn đề này và trang ReSharper cũng giải thích những điều ở đây . Nó có một số mã mẫu yêu cầu bạn làm điều này thay thế:

IEnumerable<string> names = GetNames().ToList();

Câu hỏi của tôi là về đề xuất cụ thể này: điều này sẽ không dẫn đến việc liệt kê thông qua bộ sưu tập hai lần trong 2 vòng lặp cho mỗi lần?


Điều này có trả lời câu hỏi của bạn không? Xử lý cảnh báo cho nhiều liệt kê có thể có của IEnumerable
RBT

Câu trả lời:


184

GetNames()trả về một IEnumerable. Vì vậy, nếu bạn lưu trữ kết quả đó:

IEnumerable foo = GetNames();

Sau đó, mỗi khi bạn liệt kê foo, GetNames()phương thức được gọi lại (không phải theo nghĩa đen, tôi không thể tìm thấy liên kết giải thích đúng chi tiết, nhưng hãy xem IEnumerable.GetEnumerator()).

Resharper thấy điều này và đề xuất bạn lưu trữ kết quả của việc liệt kê GetNames()trong một biến cục bộ, ví dụ bằng cách cụ thể hóa nó trong một danh sách:

IEnumerable fooEnumerated = GetNames().ToList();

Điều này sẽ đảm bảo rằng GetNames()kết quả chỉ được liệt kê một lần, miễn là bạn tham khảo fooEnumerated.

Điều này rất quan trọng vì bạn thường chỉ muốn liệt kê một lần, chẳng hạn như khi GetNames()thực hiện một lệnh gọi cơ sở dữ liệu (chậm).

Bởi vì bạn đã cụ thể hóa các kết quả trong một danh sách, việc bạn liệt kê fooEnumeratedhai lần không còn quan trọng nữa ; bạn sẽ lặp lại danh sách trong bộ nhớ hai lần.


Ah! Giải thích nó.
user2250250

1
Không. Phương thức GetEnumerator () sẽ chỉ được gọi một lần trong vòng lặp foreach. Nguyên nhân thực sự là do nguy cơ dữ liệu bẩn. Ví dụ, trong GetNames (), có một truy vấn SQL, nhưng chỉ truy vấn trả về IEnurable. Khi bạn gọi .ToList (), bạn lưu trữ tất cả dữ liệu trong bộ nhớ, ít có nguy cơ dữ liệu bẩn. Nhưng nếu có nhiều thời gian giữa 2 lần lựa chọn vòng lặp, nếu bạn thực thi SQL vào cơ sở dữ liệu mỗi lần thì sẽ có nguy cơ lớn về dữ liệu bẩn.
Sun Robin

6
Đây là url từ Microsoft để cho chúng ta biết cách triển khai bên trong của vòng lặp foreach. Bạn có thể thấy rằng GetEnumerator () chỉ được gọi một lần. Và một điều nữa chúng ta cần biết là IEnumerable đang lười tải khi nó đang làm việc với một số ORM. msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx Nếu bạn chỉ nhận được trình xử lý của Enumerator và không tải tất cả dữ liệu vào bộ nhớ, bạn có thể có một số xung đột giữa 2 hoạt động với cùng một điều tra viên, ai đó đã sửa đổi cơ sở dữ liệu. Để tránh điều này, Resharper đề nghị bạn tải dữ liệu vào Merroy thông qua phương thức .ToList ().
Sun Robin

6
Câu trả lời này là hơi không chính xác. Không phải mọi thứ IEnumerableđều được đánh giá cho mỗi kiểu liệt kê, chỉ những người được triển khai bằng 'thực thi hoãn lại' (Đó là liên kết mà bạn thiếu, @CodeCaster). Cảnh báo R # có thể được bỏ qua một cách an toàn khi việc triển khai bên dưới không được thực thi trì hoãn, như với ví dụ câu hỏi này, lệnh gọi ToList()lưu trữ nó trong một IEnumerablebiến khác .
Frédéric

2
@CodeCaster, không sao, tôi đã đến từ một câu trả lời gần đây liên kết đến đó và tôi đã thêm vào đó một lời giải thích ngắn gọn về những gì còn thiếu imo. Hãy sử dụng lại nó nếu bạn thích. Sau đó, tôi có thể sẽ xóa bình luận của tôi ở đây khi trí thông minh của họ không còn phù hợp nữa.
Frédéric

11

Tôi thấy đây là cách tốt nhất và dễ nhất để hiểu nhiều kiểu liệt kê.

C # LINQ: Có thể có nhiều kiểu liệt kê IEnumerable

https://helloacm.com/c-linq-possible-multiple-enumeration-of-ienumerable-resharper/


Bạn có thể chỉ ra những điểm đặc biệt của liên kết của mình ở đây .. nhưng liên kết rất đẹp -> +1
LuckyLikey

Đây là một lời giải thích tuyệt vời và dễ dàng với một ví dụ!
dịch ngược

9

GetNames()không được gọi hai lần. Việc triển khai của IEnumerable.GetEnumerator()được gọi mỗi khi bạn muốn liệt kê bộ sưu tập với foreach. Nếu trong IEnumerable.GetEnumerator()một số tính toán đắt tiền được thực hiện, đây có thể là một lý do để xem xét.


5

Có, chắc chắn bạn sẽ liệt kê nó hai lần. nhưng vấn đề là nếu GetNames()trả về một truy vấn linq lười biếng mà rất tốn kém để tính toán thì nó sẽ tính toán hai lần mà không cần gọi tới ToList()hoặc ToArray().

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.