给出以下代码:
public class C { public void M() { var x = 5; Action<int> action = y => Console.WriteLine(y); } }
使用VS2013,.NET 4.5。查看反编译的代码时,我们可以看到编译器正在将委托缓存 在调用站点中:
public class C { [CompilerGenerated] private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1; public void M() { if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null) { C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0); } Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1; } [CompilerGenerated] private static void <M>b__0(int y) { Console.WriteLine(y); } }
查看在Roslyn中反编译的同一代码(使用TryRoslyn),将产生以下输出:
public class C { [CompilerGenerated] private sealed class <>c__DisplayClass0 { public static readonly C.<>c__DisplayClass0 CS$<>9__inst; public static Action<int> CS$<>9__CachedAnonymousMethodDelegate2; static <>c__DisplayClass0() { // Note: this type is marked as 'beforefieldinit'. C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0(); } internal void <M>b__1(int y) { Console.WriteLine(y); } } public void M() { Action<int> arg_22_0; if (arg_22_0 = C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null) { C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 = new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1); } } }
现在,我们可以看到委托已被提升到内部的私有类中C,这是我们在关闭实例变量/字段(关闭)时经常看到的类似行为。
C
我知道 这是一个实施细节,随时可能更改。
我仍然想知道,将委托提升到一个新类并在简单地将其缓存在呼叫站点的地方缓存在那里有什么好处?
编辑:
此问题讨论的行为与此处要求的相同。
是。最重要的部分是包含lambda实现的方法现在是实例方法。
您可以看到一个代表作为中间人的委托,它通过Invoke接收实例调用,并根据实现方法的调用约定调度该调用。
请注意,有一些平台ABI要求,这些要求指定如何传递参数,如何返回结果,通过寄存器传递哪些参数以及在哪些参数中传递“ this”,等等。违反这些规则可能会对依赖堆栈遍历的工具(例如调试器)产生严重影响。
现在,如果实现方法是实例方法,则在委托内部唯一需要发生的就是将“ this”(即在调用时的委托实例)修补为封闭的Target对象。此时,由于其他所有内容都已经存在,因此委托可以直接跳转到实现方法主体。在许多情况下,这比在实现方法是静态方法的情况下需要做的工作少得多。