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 action1
và action2
đã 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 a
và b
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: here và here.
Điều này có thể là do việc triển khai C# đóng cửa không? –
Đó 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
@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ó :) –