2015-12-16 20 views
9

Sử dụng Visual Studio 2013, tôi đang cố tạo lại hình ảnh xác thực được đề cập trong bài đăng trên blog của Eric Lippert "Closing over the loop variable considered harmful".Sao chép "đóng trên biến của một foreach" gotcha

Trong thuộc tính dự án, tôi đã chọn "C# 3.0" làm phiên bản ngôn ngữ (Xây dựng> Nâng cao ...). Hơn nữa, tôi đã chọn "Khuôn khổ .NET 3.5" làm khung mục tiêu, như thể tôi nghĩ điều này không cần thiết vì điều này chỉ liên quan đến ngôn ngữ.

Chạy mã của mình:

using System; 
using System.Collections.Generic; 

namespace Project1 
{ 
    class Class1 
    { 
     public static void Main(string[] args) 
     { 
      var values = new List<int>() { 100, 110, 120 }; 
      var funcs = new List<Func<int>>(); 
      foreach (var v in values) 
      { 
       funcs.Add(() => v); 
      } 
      foreach (var f in funcs) 
       Console.WriteLine(f()); 
     } 
    } 
} 

sản lượng dự kiến:

 
120 
120 
120 

đầu ra thực tế:

 
100 
110 
120 

Như đã trả lời bởi Eric Lippert himself trong "Is there a reason for C#'s reuse of the variable in a foreach?":

Vòng lặp for sẽ không bị thay đổi và thay đổi sẽ không được "quay lại" thành phiên bản trước của C#. Do đó, bạn nên tiếp tục cẩn thận khi sử dụng thành ngữ này.

Tôi đang làm gì sai?

Trả lời

9

Câu trả lời của Scott là đúng, nhưng có thể sử dụng một số làm rõ thêm.

Vấn đề ở đây là chuyển đổi "phiên bản ngôn ngữ" không làm những gì bạn nghĩ. Điều này là theo ý kiến ​​của tôi một chút của một sự không hài lòng, vì nó là khá gây hiểu nhầm. Chuyển đổi "phiên bản ngôn ngữ" không có nghĩa là "sử dụng trình biên dịch cũ"; nó không phải là một chế độ tương thích.

Thay vào đó, nó có nghĩa là "sử dụng trình biên dịch hiện tại và tạo ra lỗi nếu tôi sử dụng tính năng không có sẵn trong phiên bản ngôn ngữ đã chọn". Lý do cho việc chuyển đổi này là để một người trong nhóm dev có thể "thử" phiên bản mới của trình biên dịch để đảm bảo rằng mã của họ vẫn hoạt động, nhưng biết trước khi họ kiểm tra rằng họ đã không vô tình sử dụng một tính năng ngôn ngữ mà các trình biên dịch của đồng đội của họ sẽ bị nghẹt thở. Vì vậy, nếu bạn đặt phiên bản ngôn ngữ thành 3.0 thì "động" sẽ không hoạt động, (vì nó được thêm vào trong C# 4.0) nhưng nó vẫn là phiên bản của trình biên dịch bạn đã cài đặt.

Khi Scott chỉ ra, nếu bạn muốn sử dụng trình biên dịch cũ, bạn sẽ phải thực sự tìm thấy một bản sao trình biên dịch cũ trên máy của bạn ở đâu đó và sử dụng nó một cách rõ ràng.

Xem http://ericlippert.com/2013/04/04/what-does-the-langversion-switch-do/ để biết thêm một số ví dụ về công tắc này thực hiện và không làm.

+0

Khi nào mã thực sự sẽ bị ngắt? Tôi đoán nếu mã dựa trên một lỗi trong trình biên dịch cũ mà sau đó được cố định hoặc nếu trình biên dịch mới giới thiệu một lỗi. Có những tình huống khác mà điều này có thể hữu ích không? – xehpuk

+0

@xehpuk: Tôi không chắc tôi hiểu câu hỏi của bạn. Nó chắc chắn là trường hợp mã dựa trên các lỗi trình biên dịch cố định sẽ phá vỡ bất kể giá trị của chuyển đổi langversion. Nhóm biên dịch sẽ cố gắng hết sức để duy trì khả năng tương thích ngược khi sửa lỗi nhưng không phải lúc nào cũng có thể. –

+0

Bạn nói đúng, nó vẫn là trình biên dịch tương tự. Vì vậy, trường hợp sử dụng về cơ bản là một thành viên dự án có phiên bản trình biên dịch mới hơn nhưng không thể dễ dàng sử dụng phiên bản cũ hơn (vì ví dụ: anh/cô ấy đang sử dụng phiên bản mới hơn của VS không có tùy chọn đó vì bất kỳ lý do gì). – xehpuk

8

Tôi tin rằng ngay cả khi bạn chọn C# 3.0 cho ngôn ngữ của bạn thì trình biên dịch vẫn thực hiện hành vi mới, tôi không nghĩ bạn có thể tạo lại hành vi cũ trong trình biên dịch mới. Điều duy nhất thiết lập ngôn ngữ là hạn chế bạn sử dụng các tính năng ngôn ngữ được giới thiệu ở phiên bản sau của ngôn ngữ.

Bạn phải tìm bản sao trình biên dịch cũ (VS 2012 trở lên) để nhận được hành vi. Khi Eric nói "sự thay đổi sẽ không được" chuyển về "các phiên bản trước của C#", anh ta có nghĩa là họ sẽ không phát hành bản cập nhật cho VS2012 để thay đổi hành vi trong trình biên dịch đó (Khi bài viết bạn trích dẫn đã viết VS2013 vừa xuất hiện và VS2012 vẫn đang được sử dụng rộng rãi).

EDIT: Nếu bạn thực sự muốn tạo lại hành vi bạn cần để viết hướng dẫn riêng cho từng vòng lặp.

public static void Main(string[] args) 
{ 
    var values = new List<int>() { 100, 110, 120 }; 
    var funcs = new List<Func<int>>(); 

    using (var enumerator = values.GetEnumerator()) 
    { 
     int v; 
     while (enumerator.MoveNext()) 
     { 
      //int v; // In C# 5+ the variable is declared here. 
      v = enumerator.Current; 
      funcs.Add(() => v); 
     } 
    } 

    foreach (var f in funcs) 
     Console.WriteLine(f()); 
} 

Điều này sẽ tạo ra kết quả mong đợi của bạn.

+4

Bạn sẽ không phải cài đặt phiên bản cũ của VS cho ví dụ về đồ chơi. Bạn có thể làm điều đó bằng cách chạy csc thủ công từ C: \ Windows \ Microsoft.NET \ Framework \ v3.5 \ csc.exe –

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