2011-12-27 18 views
10

ReSharper 6.0 mang lại cho tôi cảnh báo "Truy cập đóng cửa đã sửa đổi" cho số nhận dạng dr trong đoạn mã đầu tiên."Truy cập vào đóng cửa đã sửa đổi" có được giải quyết bằng cú pháp hiểu không?

private IEnumerable<string> GetTheDataTableStrings(DataTable dt) { 
    foreach (DataRow dr in dt.Rows) { 
     yield return GetStringFuncOutput(() => dr.ToString()); 
    } 
} 

Tôi nghĩ rằng tôi có một sự hiểu biết cơ bản về những gì cảnh báo này đang cố gắng bảo vệ tôi khỏi: dr thay đổi nhiều lần trước khi ra GetTheDataTableStrings được thẩm vấn, và do đó người gọi có thể không nhận được đầu ra/hành vi của tôi mong đợi.

Nhưng R # không cho tôi bất kỳ cảnh báo nào đối với đoạn mã thứ hai.

private IEnumerable<string> GetTheDataTableStrings(DataTable dt) { 
    return from DataRow dr in dt.Rows select GetStringFuncOutput(dr.ToString); 
} 

Tôi có an toàn khi loại bỏ cảnh báo/quan ngại này khi sử dụng cú pháp hiểu không?

mã khác:

string GetStringFuncOutput(Func<string> stringFunc) { 
    return stringFunc(); 
} 
+0

Tôi đã phải chà/đơn giản hóa mã này trước khi trình bày. Hãy cho tôi biết nếu có điều gì đó về mã ngăn bạn thảo luận câu hỏi. – lance

Trả lời

21

Trước hết, bạn là chính xác phải được quan tâm về phiên bản đầu tiên. Mỗi đại biểu được tạo bởi lambda đó được đóng trên cùng biến số và do đó khi biến đó thay đổi, ý nghĩa của truy vấn thay đổi.

Thứ hai, FYI chúng tôi rất có khả năng khắc phục sự cố này trong phiên bản tiếp theo của C#; đây là một điểm đau lớn cho các nhà phát triển.

Trong phiên bản tiếp theo mỗi lần bạn chạy qua vòng lặp "foreach", chúng tôi sẽ tạo một biến vòng lặp mới thay vì đóng cùng một biến mỗi lần. Đây là một thay đổi "phá vỡ" nhưng trong phần lớn các trường hợp, "break" sẽ được sửa chữa thay vì gây ra lỗi.

Vòng lặp "for" sẽ không bị thay đổi.

Xem http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ để biết chi tiết.

Thứ ba, không có vấn đề với phiên bản đọc truy vấn vì không có biến đóng được sửa đổi. Hình thức hiểu truy vấn cũng giống như nếu bạn muốn nói:

return dt.Rows.Select(dr=>GetStringFuncOutput(dr.ToString)); 

Lambda là không đóng cửa đối với bất kỳ biến bên ngoài, vì vậy không có biến phải được sửa đổi vô tình.

+0

Câu trả lời hay và tin tốt về các bản sửa lỗi trong C#. Phiên bản nào bạn mong đợi để có được nó trong - C# 5 hoặc 6? –

+1

@the_joric: Không có sản phẩm C# 6 nào được công bố. Chúng tôi hy vọng sẽ sửa lỗi này trong C# 5. (Chúng tôi đã phải rejigger mã viết lại đóng cửa anyways để thực hiện công việc async/await, do đó, figured cũng có thể nhận được cố định này cùng một lúc.) –

+0

Cảm ơn bạn đã cập nhật Eric :). Đó chắc chắn là tin tốt. –

4

Vấn đề mà Resharper đang cảnh báo về đã được giải quyết trong cả C# 5.0 và VB.Net 11.0. Sau đây là các chiết xuất từ ​​các đặc tả ngôn ngữ. Lưu ý rằng các thông số kỹ thuật có thể được tìm thấy trong các đường dẫn sau theo mặc định trên một máy tính với Visual Studio 2012 được cài đặt.

  • C: \ Program Files (x86) \ Microsoft Visual Studio 11.0 \ VB \ Thông số kỹ thuật \ 1033 \ Visual Basic Ngôn ngữ Specification.docx
  • C: \ Program Files (x86) \ Microsoft Visual Studio 11.0 \ VC# \ Thông số kỹ thuật \ 1033 \ CSharp Ngôn ngữ Specification.docx

C# Language Specification Version 5,0

8,8.4 Tuyên bố foreach

Vị trí v bên trong vòng lặp while rất quan trọng đối với cách thức bất kỳ chức năng ẩn danh nào diễn ra trong câu lệnh nhúng.

Ví dụ:

int[] values = { 7, 9, 13 }; 
Action f = null; 
foreach (var value in values) 
{ 
    if (f == null) f =() => Console.WriteLine("First value: " + value); 
} 
f(); 

Nếu v được tuyên bố bên ngoài vòng lặp while, nó sẽ được chia sẻ giữa tất cả các lần lặp lại, và giá trị của nó sau khi vòng lặp for sẽ là giá trị cuối cùng, 13 , đó là điều mà lệnh gọi f sẽ in. Thay vào đó, bởi vì mỗi lần lặp có biến riêng v, một biến được ghi bởi f trong lần lặp đầu tiên sẽ tiếp tục giữ giá trị 7, đó là những gì sẽ được in. (Lưu ý: phiên bản trước của C# tuyên bố v bên ngoài vòng lặp while.)

Microsoft Visual Basic Ngôn ngữ Specification Version 11,0

10.9.3 For Each ... Báo cáo tiếp theo (chú giải)

Có thay đổi nhỏ về hành vi giữa phiên bản 10.0 và 11.0 của ngôn ngữ. Trước 11.0, một biến lặp mới không được tạo cho mỗi lần lặp của vòng lặp. Sự khác biệt này chỉ quan sát được nếu biến lặp được bắt bởi một lambda hoặc một biểu thức LINQ mà sau đó được gọi sau vòng lặp.

Dim lambdas As New List(Of Action) 
For Each x In {1,2,3} 
    lambdas.Add(Sub() Console.WriteLine(x) 
Next 
lambdas(0).Invoke() 
lambdas(1).Invoke() 
lambdas(2).Invoke() 

Đến Visual Basic 10.0, điều này tạo ra một cảnh báo tại thời gian biên dịch và in "3" ba lần. Đó là bởi vì chỉ có một biến duy nhất "x" được chia sẻ bởi tất cả các lần lặp của vòng lặp, và cả ba lambdas đều bắt được cùng một "x", và vào thời điểm lambdas được thực thi, nó giữ số 3. Basic 11.0, nó in "1, 2, 3". Đó là bởi vì mỗi lambda bắt một biến khác nhau "x".