2011-09-30 37 views
9

Hãy nói rằng tôi có một số mã:Đọc một IEnumerable nhiều lần

var items = ItemsGetter.GetAllItems().Where(x => x.SomeProperty > 20); 
int sum1 = items.Sum(x => x.SomeFlag == true); 

Và ví dụ tôi cần một số tiền khác từ bộ sưu tập các mặt hàng sau này trong mã.

int sum2 = items.Sum(x => x.OtherFlag == false); 

Vì vậy, câu hỏi của tôi: Bạn có thể gọi các phương thức Linq trên IEnumerable nhiều lần không? Có lẽ tôi nên gọi Reset() phương pháp trên điều tra hoặc làm cho danh sách từ các mặt hàng bằng cách sử dụng phương pháp ToList?

Trả lời

16

Vâng, nó thực sự phụ thuộc vào những gì bạn muốn làm. Bạn có thể lấy các hit của thực hiện truy vấn hai lần (và ý nghĩa chính xác về điều đó sẽ phụ thuộc vào những gì GetAllItems() không), hoặc bạn có thể mất các hit của sao chép kết quả vào một danh sách:

var items = ItemsGetter.GetAllItems().Where(x => x.SomeProperty > 20).ToList(); 

Khi nó trong một danh sách, rõ ràng nó không phải là một vấn đề để lặp qua danh sách đó nhiều lần.

Lưu ý rằng bạn không thể gọi Reset vì bạn không có trình lặp - bạn có IEnumerable<T>. Tôi sẽ không khuyên bạn nên gọi IEnumerator<T> nói chung anyway - nhiều triển khai (bao gồm bất kỳ được tạo ra bởi trình biên dịch C# từ khối lặp) không thực sự thực hiện Reset anyway (tức là họ ném một ngoại lệ).

1

LINQ sử dụng thực thi hoãn lại, vì vậy 'các mục' sẽ chỉ liệt kê khi bạn yêu cầu thông qua phương pháp khác. Mỗi phương thức Sum của bạn sẽ lấy O (n) để lặp qua. Tùy thuộc vào mức độ lớn danh sách các mục của bạn, bạn có thể không muốn lặp lại nó nhiều lần.

+0

Đây không phải là một trả lời cho câu hỏi, đó là "Có thể gọi các phương thức Linq trên IEnumerable nhiều lần không?" ... câu trả lời là không". –

+0

Vâng, những gì "OK" có nghĩa là một chút không rõ ràng. Bạn có thể lặp qua danh sách tuy nhiên nhiều lần bạn muốn. Tôi đề cập đến các chi tiết của điều này trong câu trả lời của tôi. Làm thế nào là nó không OK để lặp qua chúng? Đăng câu trả lời của riêng bạn nếu bạn nghĩ vậy (mặc dù đã có câu trả lời được chấp nhận). –

+0

Nó hoàn toàn rõ ràng. 'items' là một IEnumerable, không phải là một danh sách và bạn chỉ có thể duyệt qua nó một lần. Không cần phải đăng câu trả lời khác, nhưng điều quan trọng là người đọc phải hiểu rằng câu trả lời này là sai và thậm chí không chạm vào câu hỏi. –

3

Tôi thỉnh thoảng trong trường hợp tôi phải xử lý nhiều lần nhiều lần. Nếu liệt kê là tốn kém, không lặp lại và sinh ra rất nhiều dữ liệu (như một IQueryable đọc từ một cơ sở dữ liệu), liệt kê nhiều lần không phải là một lựa chọn, không phải là đệm kết quả trong bộ nhớ.

Cho đến hôm nay tôi thường kết thúc viết các lớp tổng hợp mà tôi có thể đẩy các mục trong vòng lặp foreach và cuối cùng đọc kết quả - ít thanh lịch hơn LINQ.

Nhưng chờ đã, tôi vừa nói "đẩy"? Nghe có vẻ như ... phản ứng không? Vì vậy, tôi đã suy nghĩ trong khi đi bộ đêm nay. Trở về nhà tôi đã thử nó - và nó hoạt động!

Ví dụ đoạn mã cho thấy làm thế nào để có được cả hai mức tối thiểu và các mặt hàng tối đa từ một chuỗi các số nguyên trong một pass duy nhất, sử dụng khai thác LINQ chuẩn (những Rx, có nghĩa là):

public static MinMax GetMinMax(IEnumerable<int> source) 
{ 
    // convert source to an observable that does not enumerate (yet) when subscribed to 
    var connectable = source.ToObservable(Scheduler.Immediate).Publish(); 

    // set up multiple consumers 
    var minimum = connectable.Min(); 
    var maximum = connectable.Max(); 

    // combine into final result 
    var final = minimum.CombineLatest(maximum, (min, max) => new MinMax { Min = min, Max = max }); 

    // make final subscribe to consumers, which in turn subscribe to the connectable observable 
    var resultAsync = final.GetAwaiter(); 

    // now that everybody is listening, enumerate! 
    connectable.Connect(); 

    // result available now 
    return resultAsync.GetResult(); 
} 
Các vấn đề liên quan