2010-08-05 34 views
45

Bẫy biến ngoài là gì? Giải thích và ví dụ trong C# được đánh giá cao.Bẫy biến bên ngoài

EDIT: Kết hợp diktat Jon Skeet :)

Eric Lippert on the Outer Variable Trap

+3

Tôi không biết bạn đang nói gì cho đến khi tôi googled nó; Khi làm như vậy, tôi đã tìm thấy rất nhiều giải thích và ví dụ (trong C#), vì vậy bạn đang tìm kiếm những gì khác? – Marc

+2

@Marc Có lẽ OP là một trong những người đó (có nhiều điều chắc chắn hơn, ít nhất một người nói rõ ràng nhiều lần) muốn SO có câu trả lời cho mọi câu hỏi lập trình có liên quan. Câu trả lời cho điều này dường như bị thiếu. –

+1

@Maciej, Tuyệt vời. Danh sách chính là một bước gần hơn để hoàn thành! Web thống trị, ở đây chúng tôi đến! – Marc

Trả lời

61

Các "Trap Biến Outer" xảy ra khi một nhà phát triển hy vọng giá trị của một biến được chụp bởi một biểu thức lambda hoặc đại biểu vô danh, khi thực sự biến được chụp chính nó.

Ví dụ:

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.Write("{0} ", i)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

có thể sản lượng # 1:

0 1 2 3 4 5 6 7 8 9 

có thể sản lượng # 2:

10 10 10 10 10 10 10 10 10 10 

Nếu bạn mong đợi đầu ra # 1, bạn đã rơi vào Trap biến ngoài. Bạn nhận được đầu ra # 2.

Fix:

Khai báo một "Biến Nội" để được chụp liên tục thay vì "Biến Outer" được chụp chỉ một lần.

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    var j = i; 
    actions.Add(() => Console.Write("{0} ", j)); 
} 
foreach (var action in actions) 
{ 
    action(); 
} 

Để biết thêm chi tiết, xem thêm Eric Lippert's blog.

+1

Thú vị chơi chữ trên từ "bẫy": Biến được chụp (bị mắc kẹt), và bạn bị bắt bởi một vấn đề (rơi vào một cái bẫy) –

+4

Cho rằng điều này có khả năng trở thành câu trả lời được chấp nhận, bất kỳ cơ hội nào bạn thêm một liên kết đến bài đăng trên blog của Eric Lippert? http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx –

+0

Vì vậy, j về cơ bản là biến 'mới', và do đó hành động thứ k chứa một biến j_k liên kết với giá trị k-th được giả định bởi biến vòng lặp. Kết quả là hành vi mong đợi thu được. –

4

Something như

foreach (var s in strings) 
    var x = results.Where(r => (r.Text).Contains(s)); 

Sẽ không cung cấp cho các kết quả mà bạn đang mong đợi vì Có không được thực hiện cho mỗi lần lặp. Gán s cho một biến tạm thời bên trong vòng lặp sẽ sửa lỗi này.

+0

Tôi không quen thuộc với cú pháp 'var =', điều đó làm gì? = P – Marc

+0

Nitpick: 'Contains' được thực thi cho mỗi lần lặp, nhưng' s' sẽ bất ngờ luôn có cùng giá trị. – dtb

+0

@dtb @Marc Vâng, những gì dtb nói. Có lẽ tôi không giải thích rõ điều đó. var chỉ là một thay thế cho khai báo một cách rõ ràng kiểu trả về (trong mã thực, tôi thực sự khai báo kiểu trả về ở đây nhưng tôi không ở trước một IDE và không cảm thấy như tìm kiếm những gì thực sự trả về ... Tôi nghĩ IEnumberable của nó <>) – heisenberg

1

@dtb là chính xác (+1 lớn), nhưng điều quan trọng cần lưu ý là điều này chỉ áp dụng nếu phạm vi của việc đóng cửa kéo dài ngoài vòng lặp. Ví dụ:

var objects = new [] 
    { 
     new { Name = "Bill", Id = 1 }, 
     new { Name = "Bob", Id = 5 }, 
     new { Name = "David", Id = 9 } 
    }; 

for (var i = 0; i < 10; i++) 
{ 
    var match = objects.SingleOrDefault(x => x.Id == i); 

    if (match != null) 
    { 
     Console.WriteLine("i: {0} match: {1}", i, match.Name); 
    } 
} 

này sẽ in:

i: 1 match: Bill 
i: 5 match: Bob 
i: 9 match: David

ReSharper sẽ cảnh báo về "Tiếp cận đóng cửa sửa đổi," có thể được bỏ qua một cách an toàn trong trường hợp này.

0

Bài viết này giải thích các khái niệm về việc đóng cửa là hữu ích:

http://en.wikipedia.org/wiki/Closure_(computer_science)

Ngoài ra, bài viết này là thực sự tốt từ một cụ thể hơn C# thực hiện:

http://blogs.msdn.com/b/abhinaba/archive/2005/08/08/448939.aspx

Dù sao, tl lr là phạm vi biến đó cũng quan trọng trong các biểu thức đại biểu hoặc lambda ẩn danh như nó ở bất kỳ nơi nào khác trong mã của bạn - hành vi này không rõ ràng.

0

Nó xứng đáng để lưu ý rằng cái bẫy này tồn tại cho foreach vòng quá nhưng has been changed từ C# 5.0, ví dụ: bên foreach vòng đóng cửa bây giờ gần hơn một bản sao tươi của biến vòng lặp mỗi lần. Vì vậy, các mã bên dưới:

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()); 

Prints 120 120 120< C# 5.0, nhưng 100 110 120> = C# 5.0

Tuy nhiên for vòng vẫn cư xử theo cùng một cách.

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