2013-04-28 26 views
39

này hoạt động tốt (có nghĩa như mong đợi) trong C# 5.0:chụp Đóng cửa (Vòng Variable) trong C# 5.0

var actions = new List<Action>(); 
foreach (var i in Enumerable.Range(0, 10)) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

Prints từ 0 đến 9. Tuy nhiên, một điều này cho thấy 10 trong 10 lần:

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

Câu hỏi: Đây là sự cố mà chúng tôi có trong các phiên bản C# trước phiên bản 5.0; vì vậy chúng tôi đã phải sử dụng một trình giữ chỗ cục bộ vòng lặp cho việc đóng và nó đã được sửa ngay bây giờ - trong C# 5.0 - trong các vòng "foreach". Nhưng không phải trong "cho" vòng!

Lý do đằng sau điều này (không khắc phục sự cố cho các vòng lặp for)?

+2

Bạn có nghĩa là "lý do cho nó không được cố định cho các vòng' for' "không? –

+0

Tôi ngạc nhiên vì nó thậm chí còn hoạt động trong trường hợp đầu tiên ... Vì bạn chạy Action sau khi thoát khỏi phạm vi của foreach/for. 'Var i' không tồn tại nữa. Với tôi, đó là một thiết kế rất "nguy hiểm". – LightStriker

+0

@LightStriker: Không; đó là một tính năng. Nó được gọi là một đóng cửa. – SLaks

Trả lời

37

Lý do đằng sau điều này là gì?

Tôi sẽ giả sử bạn có nghĩa là "tại sao nó không thay đổi cho vòng lặp for?"

Câu trả lời là dành cho các vòng for, hành vi hiện tại có ý nghĩa hoàn hảo. Nếu bạn phá vỡ một vòng lặp for thành:

  • initializer
  • trạng
  • iterator
  • cơ thể

... sau đó vòng lặp là khoảng:

{ 
    initializer; 
    while (condition) 
    { 
     body; 
     iterator; 
    } 
} 

(Ngoại trừ việc iterator được thực thi cuối cùng của câu lệnh continue; cũng vậy.)

Phần khởi tạo logic chỉ chỉ xảy ra một lần, vì vậy hoàn toàn hợp lý là chỉ có một "biến thể biến". Hơn nữa, không có giá trị "ban đầu" tự nhiên của biến trên mỗi vòng lặp của vòng lặp - không có gì để nói rằng vòng lặp for có dạng là khai báo biến trong trình khởi tạo, thử nghiệm nó trong điều kiện và sửa đổi nó trong trình vòng lặp. Những gì bạn mong chờ một vòng như thế này để làm:

for (int i = 0, j = 10; i < j; i++) 
{ 
    if (someCondition) 
    { 
     j++; 
    } 
    actions.Add(() => Console.WriteLine(i, j)); 
} 

Hãy so sánh điều đó với một vòng lặp foreachtrông như bạn đang khai báo một biến riêng biệt cho mỗi lần lặp. Heck, biến là chỉ đọc, làm cho nó thậm chí còn nhiều hơn lẻ để nghĩ rằng đó là một biến thay đổi giữa các lần lặp. Nó có ý nghĩa hoàn hảo khi nghĩ đến vòng lặp foreach khi khai báo một biến chỉ đọc mới trên mỗi lần lặp với giá trị của nó được lấy từ trình lặp.

+5

(Thật khó để tôi tranh luận với một nhà phát triển cấp cao với một 559k bên cạnh hình ảnh của mình, nhưng :) Cảm giác thông thường (phần giới hạn trong ranh giới của tâm trí của tôi) có xu hướng không đồng ý và bằng nhiều ngôn ngữ khác bạn có thể thấy cho các hành vi như cả cho và foreach. Hãy viết lại vòng lặp while theo cách này: var x = initializer(); while (condition) {feed_x_to_body (body); x = iterator(); } và tôi hy vọng rằng feed_x_to_body (đó là một công việc cho trình biên dịch) hiện nó công việc như mong đợi (không phải là những gì là hợp lý theo thực hiện cơ bản - đó là quan tâm của trình biên dịch không phải tôi!). Có lẽ tôi đã sai lầm; xin vui lòng hướng dẫn thêm. –

+0

@KavehShahbazian: Không, đó không phải là cách một vòng lặp 'for' hoạt động. Thực tế là nhiều 'vòng lặp' * xảy ra * để khai báo chính xác một biến trong trình khởi tạo là không liên quan. Biến được khai báo trong initializer * là * biến được sử dụng trong phần thân của vòng lặp - không phải là phần thân được gán một giá trị * biến *. Đặc biệt, phần thân của vòng lặp có thể * sửa đổi * biến đó, điều này sẽ phá vỡ mô hình của bạn về thế giới. –

+0

"Có thể sửa đổi biến" đã làm cho nó rõ ràng với tôi.Tuy nhiên, tôi nghĩ rằng khi tôi tạo ra một phạm vi mới (giống như một cơ thể lambda) giá trị của biến nên bị tách ra khỏi phạm vi chính của nó. Phần đó vẫn chưa tìm thấy nó nằm trong tâm trí của tôi và sẽ rất tốt để bạn hướng dẫn thêm về phần đó. Nhưng tôi đánh dấu phản ứng của bạn là câu trả lời vì nó phù hợp (hiện tại) C# thế giới. Cảm ơn; –

Các vấn đề liên quan