Có hai phần cho cảnh báo này. Đầu tiên là...
Quyền truy cập vào biến foreach khi đóng
... mà không phải là không hợp lệ nhưng thoạt nhìn nó phản trực quan. Nó cũng rất khó để làm đúng. (Nhiều đến mức bài báo tôi liên kết đến dưới đây mô tả điều này là "có hại".)
Thực hiện truy vấn của bạn, lưu ý rằng mã bạn đã trích về cơ bản là một dạng mở rộng của những gì trình biên dịch C # (trước C # 5) tạo ra cho foreach1 :
Tôi [không] hiểu tại sao [điều sau] không hợp lệ:
string s; while (enumerator.MoveNext()) { s = enumerator.Current; ...
Vâng, nó là hợp lệ về mặt cú pháp. Và nếu tất cả những gì bạn đang làm trong vòng lặp của mình đang sử dụng giá trị của sthì mọi thứ đều tốt. Nhưng đóng cửa ssẽ dẫn đến hành vi phản trực giác. Hãy xem đoạn mã sau:
var countingActions = new List<Action>();
var numbers = from n in Enumerable.Range(1, 5)
select n.ToString(CultureInfo.InvariantCulture);
using (var enumerator = numbers.GetEnumerator())
{
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
Console.WriteLine("Creating an action where s == {0}", s);
Action action = () => Console.WriteLine("s == {0}", s);
countingActions.Add(action);
}
}
Nếu bạn chạy mã này, bạn sẽ nhận được đầu ra bảng điều khiển sau:
Creating an action where s == 1
Creating an action where s == 2
Creating an action where s == 3
Creating an action where s == 4
Creating an action where s == 5
Đây là những gì bạn mong đợi.
Để xem thứ mà bạn có thể không mong đợi, hãy chạy đoạn mã sau ngay sau đoạn mã trên:
foreach (var action in countingActions)
action();
Bạn sẽ nhận được đầu ra bảng điều khiển sau:
s == 5
s == 5
s == 5
s == 5
s == 5
Tại sao? Bởi vì chúng tôi đã tạo ra năm hàm đều hoạt động giống nhau: in giá trị của s(mà chúng tôi đã đóng lại). Trên thực tế, chúng có cùng chức năng ("In s", "In s", "In s" ...).
Tại thời điểm chúng tôi sử dụng chúng, chúng thực hiện chính xác những gì chúng tôi yêu cầu: in giá trị của s. Nếu bạn nhìn vào giá trị đã biết cuối cùng của s, bạn sẽ thấy rằng đó là 5. Vì vậy, chúng tôi được s == 5in năm lần vào bảng điều khiển.
Đó chính xác là những gì chúng tôi yêu cầu, nhưng có lẽ không phải là những gì chúng tôi muốn.
Phần thứ hai của cảnh báo ...
Có thể có các hành vi khác nhau khi được biên dịch với các phiên bản trình biên dịch khác nhau.
... là những gì nó được. Bắt đầu với C # 5, trình biên dịch tạo mã khác "ngăn" điều này xảy ra thông quaforeach .
Do đó, đoạn mã sau sẽ tạo ra các kết quả khác nhau trong các phiên bản khác nhau của trình biên dịch:
foreach (var n in numbers)
{
Action action = () => Console.WriteLine("n == {0}", n);
countingActions.Add(action);
}
Do đó, nó cũng sẽ tạo ra cảnh báo R # :)
Đoạn mã đầu tiên của tôi, ở trên, sẽ thể hiện cùng một hành vi trong tất cả các phiên bản của trình biên dịch, vì tôi không sử dụng foreach(đúng hơn, tôi đã mở rộng nó theo cách mà các trình biên dịch trước C # 5 làm).
Đây có phải là phiên bản CLR không?
Tôi không chắc bạn đang hỏi gì ở đây.
Bài đăng của Eric Lippert nói rằng sự thay đổi xảy ra "trong C # 5". Vì thếcó lẽ bạn phải nhắm mục tiêu .NET 4.5 trở lên với trình biên dịch C # 5 trở lên để nhận hành vi mới và mọi thứ trước đó có hành vi cũ.
Nhưng nói rõ, đó là một chức năng của trình biên dịch chứ không phải phiên bản .NET Framework.
Có liên quan với IL không?
Các mã khác nhau tạo ra IL khác nhau nên theo nghĩa đó, có hậu quả đối với IL được tạo ra.
1 foreach là một cấu trúc phổ biến hơn nhiều so với mã bạn đã đăng trong nhận xét của mình. Vấn đề thường phát sinh thông qua việc sử dụng foreach, không phải thông qua cách liệt kê thủ công. Đó là lý do tại sao những thay đổi foreachtrong C # 5 giúp ngăn chặn vấn đề này, nhưng không hoàn toàn.