2012-08-17 24 views
39

Giả sử chúng ta có mã ví dụ:Tại sao trình biên dịch C# tạo ra lớp đơn để nắm bắt các biến của một số lambdas?

public class Observer 
{ 
    public event EventHandler X = delegate { }; 
} 

public class Receiver 
{ 
    public void Method(object o) {} 
} 

public class Program 
{ 
    public static void DoSomething(object a, object b, Observer observer, Receiver r) 
    { 
     var rCopy = r; 
     EventHandler action1 = (s, e) => rCopy.Method(a); 
     EventHandler action2 = (s, e) => r.Method(b); 
     observer.X += action1; 
     observer.X += action2; 
    } 

    public static void Main(string[] args) 
    { 
     var observer = new Observer(); 
     var receiver = new Receiver(); 
     DoSomething(new object(), new object(), observer, receiver); 
    } 
} 

Đây action1action2 đã hoàn toàn tách biệt tập hợp các biến bị bắt - rCopy đã được tạo ra đặc biệt cho việc này. Tuy nhiên, trình biên dịch tạo ra chỉ một lớp để nắm bắt tất cả mọi thứ (kiểm tra tạo ra IL). Tôi cho rằng nó được thực hiện vì lý do tối ưu, nhưng nó cho phép lỗi rò rỉ bộ nhớ rất khó: nếu ab bị bắt trong lớp đơn, GC không thể thu thập cả hai ít nhất là bất kỳ số nào của lambdas được tham chiếu.

Có cách nào thuyết phục trình biên dịch tạo ra hai lớp chụp khác nhau không? Hoặc bất kỳ lý do gì tại sao nó không thể được thực hiện?

P.S. Chi tiết hơn một chút, trong blog của tôi: herehere.

+0

Điều này có thể là do việc triển khai C# đóng cửa không? –

+0

Đó là một điểm tốt, nhưng nó sẽ không được sạch hơn để đơn giản hóa DoSomething để có một đối tượng duy nhất và gọi nó hai lần? Sau đó, bạn sẽ nhận được hai trường hợp riêng biệt của trình bao bọc được tạo. – lesscode

+0

@Wayne Nó là một trường hợp rất đơn giản chỉ để chứng minh vấn đề. Trong trường hợp thực tế, tôi đã kết thúc bằng cách viết 'lớp đóng cửa' (tương tự như biên dịch viết lại) bằng tay và vấn đề này đã được giải quyết. Nhưng mã được chia thành nhiều phần và khó đọc hơn. Vâng, kết quả là xa làm cho tôi tự hào về nó :) –

Trả lời

35

Bạn đã tái phát hiện một nhược điểm nổi tiếng trong việc thực hiện chức năng ẩn danh trong C#. I described the problem in my blog in 2007.

Có cách nào thuyết phục trình biên dịch tạo ra hai lớp chụp khác nhau không?

số

Hoặc bất kỳ lý do tại sao nó không thể được thực hiện?

Không có lý thuyết lý do tại sao một thuật toán cải tiến cho phân vùng biến đóng trên để họ được kéo lên vào các lớp học đóng cửa khác nhau không thể nghĩ ra. Chúng tôi đã không làm như vậy vì lý do thực tế: thuật toán phức tạp, tốn kém để có được quyền và tốn kém để kiểm tra, và chúng tôi luôn có những ưu tiên cao hơn. Hy vọng rằng sẽ thay đổi trong Roslyn, nhưng chúng tôi không đảm bảo.

26

Tôi khá chắc chắn rằng bạn đang thấy những hạn chế thực tế trong mã viết lại của trình biên dịch, nó không phải là đơn giản để làm. Cách giải quyết rất dễ dàng đủ, tạo lambda trong một phương pháp riêng biệt để bạn có được hai trường hợp riêng biệt của lớp ẩn:

public static void DoSomething(object a, object b, Observer observer, Receiver r) { 
    var rCopy = r; 
    observer.X += register(r, a); 
    observer.X += register(rCopy, b); 
} 
private static EventHandler register(Receiver r, object obj) { 
    return new EventHandler((s, e) => r.Method(obj)); 
} 
+2

Tại thời điểm này, bản sao Bộ thu có được yêu cầu không? bạn không thể lấy đi chỉ bằng cách sử dụng người nhận ban đầu trong cả hai phương pháp, và không có một Implicit chụp? – WillFM

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