2013-02-27 44 views
35

tuyên bố 1:Tại sao các đầu ra LINQ này lại khác nhau?

IEnumerable<char> query = "Not what you might expect"; 

query = query.Where (c => c != 'a'); 
query = query.Where (c => c != 'e'); 
query = query.Where (c => c != 'i'); 
query = query.Where (c => c != 'o'); 
query = query.Where (c => c != 'u'); 

Sản lượng String.Join("", query): "Nt wht y mght xpct"

2 tuyên bố:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
    query = query.Where (c => c != vowel); 

Sản lượng String.Join("", query): "Not what yo might expect"

Các đầu ra từ các báo cáo này là khác nhau. Có ai giải thích tại sao không?

+4

đầu ra bạn nhận được là gì? – Default

+1

Kết quả của việc này sẽ phụ thuộc vào phiên bản .NET bạn đang nhắm mục tiêu - phiên bản này là gì? – goric

+1

Đây thực sự là cách mã của bạn được cấu trúc? Giá trị 'nguyên âm' cần phải được nâng lên trong ví dụ thứ hai hoặc nếu nó sẽ được thực thi như'! = 'U'' 5 lần. –

Trả lời

57

Nếu bạn đang sử dụng C# phiên bản thấp hơn 5.0 (nơi này đã được cố định), đây là lý do:

Lambda trong truy vấn của bạn nắm bắt được vòng lặp biến vowel.
Vì LINQ thích sử dụng thực thi trì hoãn, giá trị của tham chiếu này không được đọc cho đến khi truy vấn được thực hiện (bằng cách lặp lại nó), sau khi vòng lặp foreach kết thúc. Tại thời điểm đó, giá trị gần đây nhất của vowelu, đó là lý do tại sao bạn nhận được kết quả không mong muốn.

Bạn có thể thực hiện việc này bằng cách sao chép giá trị sang biến tạm thời khác (hoặc bằng cách nâng cấp lên C# 5.0).

Hãy thử điều này:

query = "Probably what you might expect"; 

foreach (char vowel in "aeiou") { 
    char currentVowel = vowel; 
    query = query.Where (c => c != currentVowel); 
} 
+0

Chỉ tò mò, khi nào C# sửa lỗi này? Tôi có thể đọc về bản sửa lỗi này ở đâu? Cảm ơn. –

+3

Ồ, [Tôi hiểu] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx), trong C# 5 –

+2

Tự hỏi tại sao điều này đã được "cố định" trong .net 4.5. Vì LINQ được hoãn thi hành theo định nghĩa, tôi không thấy lý do tại sao sửa đổi hành vi cũ, điều này thực sự chính xác hơn. – Teejay

11

đọc về việc đóng. Nếu bạn sử dụng .NET 4.0 và bên dưới, bạn sẽ nhận được kết quả khác. Trong .NET 4.5 hành vi này được thay đổi (cố định). Xem thêm cách trình biên dịch mở rộng foreach.

+0

Đối với bài viết này http://msmvps.com/blogs/kathleen/archive/2012/07/03/lifting-foreach-breaking-change-in-visual-studio-2012.aspx nó có vẻ là một sự thay đổi trong VS 2012 trình biên dịch, chứ không phải trong C# 5. Trong thực tế, tôi chỉ cần thử với VB trên khuôn khổ 3.5 và nó mang lại cho * mới * dự kiến ​​kết quả. Vui lòng sửa đổi câu trả lời của bạn để không gây nhầm lẫn cho người đọc trong tương lai. – Teejay

+1

@Teejay Hành vi này được mô tả trong đặc điểm kỹ thuật C# 5.0. Ngoài ra các trình biên dịch là một phần của .NET framework. –

+1

Có thể. Nhưng bạn đã viết * "Nếu bạn sử dụng .NET 4.0 trở xuống, bạn sẽ nhận được kết quả khác." *. Đo không phải sự thật. Chỉ cần thử ngay cả trên 3.5 và nó hoạt động như mới 4.5. – Teejay

13

Đó là vì bạn tạo một đóng cửa trên biến số vowel, thay đổi theo thời gian. Lưu trữ giá trị của nó trong một biến riêng biệt và nó sẽ hoạt động:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
{ 
    var current = vowel; 
    query = query.Where (c => c != current); 
} 
Các vấn đề liên quan