Câu lệnh LINQ sau hoạt động như thế nào?


160

Câu lệnh LINQ sau hoạt động như thế nào?

Đây là mã của tôi:

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0);
list.Add(8);
foreach (var i in even)
{
    Console.WriteLine(i);
}

Đầu ra: 2, 4, 6, 8

Tại sao không 2, 4, 6?


102
Kết quả của một biểu thức truy vấn là một truy vấn, không phải là việc thực hiện truy vấn.
Eric Lippert

6
Để biết thêm thông tin, xem câu trả lời được chấp nhận cho câu hỏi này .
Daniel

9
Chắc chắn bạn có thể nghĩ về một tiêu đề thực sự tóm tắt câu hỏi.
Matt Ball

2
Tôi đoán về các downvote (6 giờ, không phải của tôi) là họ coi tiêu đề câu hỏi quá chung chung là một câu hỏi hay. Nhưng, nhìn thấy số lượng upvote và trở thành câu hỏi hàng đầu trong tuần trong bản tin, tôi không nghĩ bạn cần phải lo lắng về nó quá nhiều.
Abel

Câu trả lời:


235

Đầu ra là 2,4,6,8do thực hiện hoãn lại .

Truy vấn được thực hiện khi biến truy vấn được lặp lại, không phải khi biến truy vấn được tạo. Điều này được gọi là thực hiện hoãn lại.

- Suprotim Agarwal, "Trì hoãn so với thực thi truy vấn ngay lập tức trong LINQ"

Có một thực thi khác gọi là Thực thi truy vấn tức thời , rất hữu ích cho kết quả truy vấn bộ đệm. Từ Suprotim Agarwal một lần nữa:

Để buộc thực thi ngay một truy vấn không tạo ra giá trị đơn lẻ, bạn có thể gọi phương thức ToList(), ToDictionary(), ToArray(), Count(), Average()hoặc Max()phương thức trên một truy vấn hoặc biến truy vấn. Chúng được gọi là các toán tử chuyển đổi cho phép bạn tạo một bản sao / ảnh chụp kết quả và quyền truy cập nhiều lần bạn muốn mà không cần phải thực hiện lại truy vấn.

Nếu bạn muốn đầu ra là 2,4,6, sử dụng .ToList():

var list = new List<int>{1,2,4,5,6};
var even = list.Where(m => m%2 == 0).ToList();
list.Add(8);
foreach (var i in even)
 {
    Console.WriteLine(i);
 }

8
Đếm (), Max (), Average (), Sum () và có lẽ các phương thức khác cần xem xét toàn bộ danh sách, cũng gây ra sự đánh giá của truy vấn.
Bị bắt

1
Tôi thường nghĩ về việc có, ví dụ, 'bộ lọc được lọc' như một biến, thay vì 'bộ lọcList ()' như một phương thức - ý tưởng là, bạn lặp đi lặp lại mỗi khi bạn muốn danh sách được lọc, thay vì gọi một phương thức. Có thể là một phương pháp thú vị, nếu không bình thường và có lẽ không hoàn hảo về hiệu suất làm việc.
Katana314

4
@Sebastian - Tiếp tục đến @ bình luận Kenned của, .First(), .FirstOrDefault(), .Single().SingleOrDefault()cũng kích hoạt các đánh giá của truy vấn.
Scotty.NET 17/07/13

4
đáng kinh ngạc làm thế nào bạn có câu trả lời trong chưa đầy 30 giây: D
MC

2
@MC Tôi không biết tại sao bạn lại hỏi câu hỏi này. Toàn bộ câu trả lời đã không được đưa ra tại một thời điểm. Nó đã được chỉnh sửa nhiều lần.
Atish Dipongkor - MVP

11

Điều này đã xảy ra do thực thi hoãn lại, có nghĩa là việc tính toán biểu thức không được thực hiện cho đến khi cần ở đâu đó. Điều này làm cho hiệu suất tốt hơn nếu dữ liệu quá lớn.


3
Bạn có thể thấy điều đó, vì nó cũng có thể có nghĩa là bảng liệt kê đắt tiền của bạn đang được thực thi nhiều lần. Trong trường hợp như vậy, bạn thậm chí có thể bị mất hiệu suất.
Nỗi sợ hãi tuyệt vọng

0

Lý do cho điều này là việc thực hiện hoãn biểu thức lambda của bạn. Truy vấn được thực thi khi bạn bắt đầu lặp trong vòng lặp foreach.


11
Về mặt kỹ thuật, đó là sự thực thi bị trì hoãn của iterator chứ không phải lambda .
D Stanley

0

Khi bạn sử dụng IEnumerable <> thu được từ LINQ, chỉ được tạo một lớp Enumerator và phép lặp chỉ bắt đầu khi bạn sử dụng nó trong một số bước đi.


-1

Bạn đang nhận được kết quả này vì thực thi bị trì hoãn có nghĩa là kết quả thực sự không được đánh giá cho đến khi truy cập lần đầu tiên.

Để làm cho nó rõ ràng hơn, chỉ cần thêm 10 vào danh sách ở cuối snipet của bạn và sau đó in lại, bạn sẽ không nhận được 10 trong đầu ra

     var list = new List<int>{1,2,4,5,6};
    var even = list.Where(m => m%2 == 0).Tolist();
    list.Add(8);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }
//new*
    list.Add(10);
    foreach (var i in even)
    {
        Console.WriteLine(i);
    }

Bạn đã thực sự thử điều đó? Tôi nhận được 10trong đầu ra.
Đánh dấu

bắt tốt @MarkHurd có không thêm .ToList (). chỉnh sửa bài viết bây giờ nó sẽ cho đầu ra dự kiến. Kỳ vọng của tôi là biểu hiện chỉ được đánh giá khi bạn sử dụng var lần đầu tiên nhưng có vẻ như nó được đánh giá mọi lúc
sandeep

Bây giờ nó sẽ không chứa 8trong một trong hai đầu ra.
Đánh dấu
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.