2012-01-30 40 views
53

Tôi cần lặp qua Danh sách đối tượng, chỉ thực hiện điều gì đó cho các đối tượng có thuộc tính boolean được đặt thành true. Tôi đang tranh luận giữa mã nàyLINQ + Foreach vs Foreach + Nếu

foreach (RouteParameter parameter in parameters.Where(p => p.Condition)) 
{ //do something } 

và mã này

foreach (RouteParameter parameter in parameters) 
{ 
    if !parameter.Condition 
    continue; 
    //do something 
} 

Mã đầu tiên rõ ràng là sạch, nhưng tôi nghi ngờ nó sẽ lặp trên danh sách hai lần - một lần cho các truy vấn và một lần cho foreach. Đây sẽ không phải là một danh sách lớn vì vậy tôi không quá lo lắng về hiệu suất, nhưng ý tưởng lặp lại hai lần chỉ lỗi tôi.

Câu hỏi: Có cách nào sạch/đẹp để viết điều này mà không lặp lại hai lần?

+7

Hóa ra tôi đã hiểu lầm cách thực hiện chậm LINQ của công trình, và các hình thức thực sự giống hệt nhau trong thực hiện. Ước gì tôi có thể đánh dấu nhiều câu trả lời, bởi vì tất cả những câu trả lời dưới đây đều bổ sung thêm điều gì đó. – Joel

Trả lời

122

Jon Skeet đôi khi thực hiện một bản demo LINQ sống động để giải thích cách hoạt động của nó. Hãy tưởng tượng bạn có ba người trên sân khấu. Ở bên trái, chúng tôi có một anh chàng có một cỗ bài xáo trộn. Ở giữa chúng tôi có một người chỉ đi dọc theo thẻ đỏ, và bên phải, chúng tôi có một người muốn thẻ.

Anh chàng ở bên phải chọc anh chàng ở giữa. Anh chàng ở giữa chọc anh chàng bên trái. Người đàn ông ở bên trái đưa anh chàng vào giữa một lá bài. Nếu nó là màu đen, anh chàng ở giữa ném nó trên sàn nhà và chọc một lần nữa cho đến khi anh ta nhận được một thẻ đỏ, sau đó anh ta đưa cho anh chàng bên phải. Sau đó, người đàn ông bên phải chọc anh chàng ở giữa một lần nữa.

Điều này tiếp tục cho đến khi chàng trai ở bên trái hết thẻ.

Sàn không được chuyển từ đầu đến cuối nhiều lần. Tuy nhiên, cả hai người ở bên trái và anh chàng ở giữa xử lý 52 thẻ, và anh chàng bên phải xử lý 26 thẻ. Có tổng cộng 52 + 52 + 26 hoạt động trên thẻ, nhưng bộ bài chỉ được lặp qua một lần.

Phiên bản "LINQ" của bạn và phiên bản "tiếp tục" giống nhau; nếu bạn có

foreach(var card in deck) 
{ 
    if (card.IsBlack) continue; 
    ... use card ... 

sau đó có 52 hoạt động mà lấy mỗi thẻ từ boong tàu, 52 hoạt động thử nghiệm để xem nếu mỗi thẻ là màu đen, và 26 hoạt động mà hành động trên thẻ đỏ. Cùng một điều chính xác.

+10

+1: nền đẹp * live-action * ví dụ! –

+0

Có lẽ nên chỉ ra rằng nó không phải là ma thuật giữ danh sách này được liệt kê hai lần. Thực thi hoãn lại được đề cập trong các câu trả lời khác, vì vậy hãy xem xét các giải thích đó về * tại sao * nó hoạt động theo cách @Eric được mô tả một cách hùng hồn. – hemp

+0

Thực sự thích cách bạn giải thích điều này. – Tarik

35

Hầu hết các toán tử LINQ như Where được triển khai để hỗ trợ thực thi hoãn lại và lười biếng. Trong ví dụ của bạn, danh sách sẽ được lặp lại chỉ một lần bởi vì điều tra viên ngồi sau IEnumerable được trả về bởi Where sẽ liệt kê danh sách cho đến khi nó tìm thấy một mục phù hợp với vị từ, mang lại nó và sẽ chỉ tiếp tục khi nó được yêu cầu cho phần tử tiếp theo.

Từ góc độ mã, tôi muốn biến thể sử dụng ở đâu, mặc dù có thể lập luận rằng bạn có thể khai báo địa phương cho parameters.Where(p => p.Condition).

Loạt bài viết của Jon Skeet Edulinq được đề xuất, đọc một số bit trong số này sẽ giúp bạn hiểu rõ về các toán tử LINQ.

26

Thực ra, nó không phải "lặp lại hai lần". Mệnh đề .Where sử dụng thực thi hoãn lại. Nói cách khác, thực tế không có công việc nào được thực hiện khi bạn gọi .Where, nhưng khi bạn lặp lại kết quả, nó sẽ lặp qua danh sách gốc và chỉ chuyển qua các mục phù hợp với điều kiện của bạn. Nếu bạn nghĩ về nó về cách mã được thực hiện, bạn đang có hiệu quả làm điều này:

Func<Parameter, bool> matchesCondition = p => p.Condition; 
foreach(var parameter in parameters) 
{ 
    if(matchesCondition(parameter)) 
    { 
     ... 
    } 
} 

Như một vấn đề của phong cách, cá nhân tôi thích một cái gì đó giống như:

var matchingParameters = parameters.Where(p => p.Condition); 
foreach(var parameter in matchingParameters) 
{ 
} 
-2

tôi thích điều này:

theList.Where(itm => itm.Condition).ToList().ForEach(itmFE => { itmFe.DoSomething(); }); 
+8

Điều này * là * liệt kê danh sách hai lần. Chính xác những gì OP * không * muốn. –

+0

Và tôi tự hỏi tại sao bạn sẽ làm điều đó. Câu trả lời được đưa ra ở trên là rực rỡ – Aakash