Khi trình biên dịch nắm bắt các biến cục bộ được sử dụng bởi các phương thức ẩn danh trong phần đóng, nó làm như vậy bằng cách tạo một lớp trợ giúp cụ thể cho phạm vi của phương thức chứa định nghĩa đại biểu. Một phương thức như vậy tồn tại trên mỗi phạm vi, ngay cả khi có nhiều đại biểu trong phạm vi đó. Xem giải thích của Eric Lippert here.
Vay từ ví dụ của bạn, hãy xem xét các chương trình sau đây:
using System;
namespace ConsoleApplication
{
internal class Program
{
private static void Main(string[] args)
{
F();
}
public static void F()
{
var rnd1 = new Random();
var rnd2 = new Random();
Action a1 =() => G(rnd1);
Action a2 =() => G(rnd2);
}
private static void G(Random r)
{
}
}
}
Lấy một cái nhìn cho IL được tạo ra bởi trình biên dịch, chúng ta thấy những điều sau đây để thực hiện F()
:
.method public hidebysig static
void F() cil managed
{
// Method begins at RVA 0x205c
// Code size 56 (0x38)
.maxstack 2
.locals init (
[0] class ConsoleApplication.Program/'<>c__DisplayClass1_0' 'CS$<>8__locals0',
[1] class [mscorlib]System.Action a1,
[2] class [mscorlib]System.Action a2
)
IL_0000: newobj instance void ConsoleApplication.Program/'<>c__DisplayClass1_0'::.ctor()
IL_0005: stloc.0
IL_0006: nop
IL_0007: ldloc.0
IL_0008: newobj instance void [mscorlib]System.Random::.ctor()
IL_000d: stfld class [mscorlib]System.Random ConsoleApplication.Program/'<>c__DisplayClass1_0'::rnd1
IL_0012: ldloc.0
IL_0013: newobj instance void [mscorlib]System.Random::.ctor()
IL_0018: stfld class [mscorlib]System.Random ConsoleApplication.Program/'<>c__DisplayClass1_0'::rnd2
IL_001d: ldloc.0
IL_001e: ldftn instance void ConsoleApplication.Program/'<>c__DisplayClass1_0'::'<F>b__0'()
IL_0024: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0029: stloc.1
IL_002a: ldloc.0
IL_002b: ldftn instance void ConsoleApplication.Program/'<>c__DisplayClass1_0'::'<F>b__1'()
IL_0031: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0036: stloc.2
IL_0037: ret
} // end of method Program::F
Lưu ý hướng dẫn IL đầu tiên: IL_0000: newobj instance void ConsoleApplication.Program/'<>c__DisplayClass1_0'::.ctor()
đang gọi hàm tạo mặc định của lớp trình trợ giúp do trình biên dịch tạo ra - lớp có trách nhiệm ghi lại biến cục bộ les trong đóng cửa.
Đây là IL cho lớp helper trình biên dịch tạo:
.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public class [mscorlib]System.Random rnd1
.field public class [mscorlib]System.Random rnd2
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Method begins at RVA 0x20a3
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method '<>c__DisplayClass1_0'::.ctor
.method assembly hidebysig
instance void '<F>b__0'() cil managed
{
// Method begins at RVA 0x20ac
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Random ConsoleApplication.Program/'<>c__DisplayClass1_0'::rnd1
IL_0006: call void ConsoleApplication.Program::G(class [mscorlib]System.Random)
IL_000b: nop
IL_000c: ret
} // end of method '<>c__DisplayClass1_0'::'<F>b__0'
.method assembly hidebysig
instance void '<F>b__1'() cil managed
{
// Method begins at RVA 0x20ba
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.Random ConsoleApplication.Program/'<>c__DisplayClass1_0'::rnd2
IL_0006: call void ConsoleApplication.Program::G(class [mscorlib]System.Random)
IL_000b: nop
IL_000c: ret
} // end of method '<>c__DisplayClass1_0'::'<F>b__1'
} // end of class <>c__DisplayClass1_0
Chú ý rằng lớp helper này có các trường cho cảrnd1
và rnd2
.
Các "cuối cùng" thi hành F()
tại IL cấp cao nhất là tương tự như sau:
public static void F()
{
var closureHelper = new ClosureHelper();
closureHelper.rnd1 = new Random();
closureHelper.rnd2 = new Random();
Action a1 = closureHelper.MethodOne;
Action a2 = closureHelper.MethodTwo;
}
đâu ClosureHelper
được thực hiện tương tự như:
internal class Program
{
public class ClosureHelper
{
public Random rnd1;
public Random rnd2;
void MethodOne()
{
Program.G(rnd1);
}
void MethodTwo()
{
Program.G(rnd2);
}
}
}
Đối với lý do tại sao ReSharper không cảnh báo bạn rằng có một sự bắt giữ ngầm xảy ra trong trường hợp này, tôi không biết.
Tôi cũng không được cảnh báo về ví dụ đó, nhưng bạn chính xác rằng việc kiểm tra cho thấy việc thu thập tiềm ẩn thực sự xảy ra (đối với cả mã gốc và mã trong câu trả lời của bạn) – dlf