Việc kiểm tra này thu hút sự chú ý của bạn đến thực tế là có nhiều giá trị đóng hơn đang được nắm bắt rõ ràng hơn, điều này có tác động đến tuổi thọ của các giá trị này.
Hãy xem xét các mã sau đây:
using System;
public class Class1 {
private Action _someAction;
public void Method() {
var obj1 = new object();
var obj2 = new object();
_someAction += () => {
Console.WriteLine(obj1);
Console.WriteLine(obj2);
};
// "Implicitly captured closure: obj2"
_someAction += () => {
Console.WriteLine(obj1);
};
}
}
Trong lần đóng đầu tiên, chúng ta thấy rằng cả obj1 và obj2 đang bị bắt một cách rõ ràng; chúng ta có thể thấy điều này chỉ bằng cách nhìn vào mã. Đối với lần đóng thứ hai, chúng ta có thể thấy obj1 đang bị bắt một cách rõ ràng, nhưng ReSharper đang cảnh báo chúng ta rằng obj2 đang bị bắt ngầm.
Điều này là do một chi tiết triển khai trong trình biên dịch C #. Trong quá trình biên dịch, các bao đóng được viết lại thành các lớp với các trường chứa các giá trị đã bắt và các phương thức đại diện cho chính bao đóng. Trình biên dịch C # sẽ chỉ tạo một lớp riêng như vậy cho mỗi phương thức và nếu nhiều hơn một bao đóng được định nghĩa trong một phương thức, thì lớp này sẽ chứa nhiều phương thức, một phương thức cho mỗi bao đóng và nó cũng sẽ bao gồm tất cả các giá trị được bắt giữ từ tất cả các bao đóng.
Nếu chúng ta nhìn vào mã mà trình biên dịch tạo ra, nó trông giống như thế này (một số tên đã được xóa để dễ đọc):
public class Class1 {
[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public object obj1;
public object obj2;
internal void <Method>b__0()
{
Console.WriteLine(obj1);
Console.WriteLine(obj2);
}
internal void <Method>b__1()
{
Console.WriteLine(obj1);
}
}
private Action _someAction;
public void Method()
{
// Create the display class - just one class for both closures
var dc = new Class1.<>c__DisplayClass1_0();
// Capture the closure values as fields on the display class
dc.obj1 = new object();
dc.obj2 = new object();
// Add the display class methods as closure values
_someAction += new Action(dc.<Method>b__0);
_someAction += new Action(dc.<Method>b__1);
}
}
Khi phương thức chạy, nó tạo ra lớp hiển thị, nó nắm bắt tất cả các giá trị, cho tất cả các bao đóng. Vì vậy, ngay cả khi một giá trị không được sử dụng trong một trong các bao đóng, nó vẫn sẽ bị bắt. Đây là bản chụp "ngầm" mà ReSharper đang làm nổi bật.
Hàm ý của việc kiểm tra này là giá trị đóng cửa được chụp ngầm sẽ không được thu gom rác cho đến khi chính bao đóng là rác được thu thập. Thời gian tồn tại của giá trị này hiện được gắn với thời gian đóng của một giá trị không sử dụng rõ ràng giá trị. Nếu việc đóng cửa tồn tại lâu, điều này có thể có tác động tiêu cực đến mã của bạn, đặc biệt nếu giá trị bị bắt là rất lớn.
Lưu ý rằng mặc dù đây là chi tiết triển khai của trình biên dịch, nhưng nó nhất quán giữa các phiên bản và triển khai như Microsoft (trước và sau Roslyn) hoặc trình biên dịch của Mono. Việc triển khai phải hoạt động như được mô tả để xử lý chính xác nhiều lần đóng bắt một loại giá trị. Ví dụ, nếu nhiều bao đóng bắt một int, thì chúng phải chụp cùng một thể hiện, điều này chỉ có thể xảy ra với một lớp lồng riêng được chia sẻ. Tác dụng phụ của việc này là thời gian tồn tại của tất cả các giá trị được chụp hiện là thời gian tồn tại tối đa của bất kỳ bao đóng nào nắm bắt bất kỳ giá trị nào.