如何理解在C#中使用exception处理“使用”的JITed代码

我在C#中编写了一个非常简单的类:

class DisposableClass : IDisposable { public void Dispose() { } } static void UsingClass() { // line 31 using (var dc = new DisposableClass()) { // line 32 DoSomething(dc); // line 33 } // line 34 } // line 35 

我使用WinDBG将JIT之后的本机代码转储到它:

 0:000> !u 000007fe87d30120 Normal JIT generated code SimpleConsole.Program.UsingClass() Begin 000007fe87d30120, size 80 c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: >>> 000007fe`87d30120 55 push rbp 000007fe`87d30121 4883ec30 sub rsp,30h 000007fe`87d30125 488d6c2420 lea rbp,[rsp+20h] 000007fe`87d3012a 48896500 mov qword ptr [rbp],rsp 000007fe`87d3012e 48c7450800000000 mov qword ptr [rbp+8],0 000007fe`87d30136 488d0d6b47eeff lea rcx,[000007fe`87c148a8] 000007fe`87d3013d e8fe24665f call clr+0x2640 (000007fe`e7392640) (JitHelp: CORINFO_HELP_NEWSFAST) // new DisposableClass() 000007fe`87d30142 48894508 mov qword ptr [rbp+8],rax c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 33: 000007fe`87d30146 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d3014a e8d1beeeff call 000007fe`87c1c020 (SimpleConsole.Program.DoSomething(System.Object), mdToken: 0000000006000012) 000007fe`87d3014f 90 nop 000007fe`87d30150 90 nop c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 000007fe`87d30151 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d30155 4c8d1dc4feeeff lea r11,[000007fe`87c20020] 000007fe`87d3015c ff15befeeeff call qword ptr [000007fe`87c20020] // Call Dispose() 000007fe`87d30162 90 nop 000007fe`87d30163 488d6510 lea rsp,[rbp+10h] 000007fe`87d30167 5d pop rbp 000007fe`87d30168 c3 ret // I could understand the code above (without exception thrown). c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 32: 000007fe`87d30169 55 push rbp 000007fe`87d3016a 4883ec30 sub rsp,30h 000007fe`87d3016e 488b6920 mov rbp,qword ptr [rcx+20h] 000007fe`87d30172 48896c2420 mov qword ptr [rsp+20h],rbp 000007fe`87d30177 488d6d20 lea rbp,[rbp+20h] c:\projects\SimpleConsole\SimpleConsole\Program.cs @ 35: 000007fe`87d3017b 48837d0800 cmp qword ptr [rbp+8],0 000007fe`87d30180 7417 je 000007fe`87d30199 000007fe`87d30182 488d1597feeeff lea rdx,[000007fe`87c20020] 000007fe`87d30189 488b4508 mov rax,qword ptr [rbp+8] 000007fe`87d3018d 803800 cmp byte ptr [rax],0 000007fe`87d30190 488b4d08 mov rcx,qword ptr [rbp+8] 000007fe`87d30194 4c8bda mov r11,rdx 000007fe`87d30197 ff12 call qword ptr [rdx] 000007fe`87d30199 90 nop 000007fe`87d3019a 4883c430 add rsp,30h 000007fe`87d3019e 5d pop rbp 000007fe`87d3019f c3 ret 

我可以毫无例外地理解代码(上面评论过),但抛出exception时代码是如何工作的? 代码如何进入评论下面的代码?

更新:

有些人认为我们应该从IL开始,所以我粘贴下面的代码:

 .method private hidebysig static void UsingClass () cil managed noinlining { // Method begins at RVA 0x23bc // Code size 25 (0x19) .maxstack 1 .locals init ( [0] class SimpleConsole.DisposableClass dc ) IL_0000: newobj instance void SimpleConsole.DisposableClass::.ctor() IL_0005: stloc.0 .try { IL_0006: ldloc.0 IL_0007: call void SimpleConsole.Program::DoSomething(object) IL_000c: leave.s IL_0018 } // end .try finally { IL_000e: ldloc.0 IL_000f: brfalse.s IL_0017 IL_0011: ldloc.0 IL_0012: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0017: endfinally } // end handler IL_0018: ret } // end of method Program::UsingClass 

但我不认为这有帮助,因为IL几乎将所有内容保存在C#中,就像完整的try...finally语句一样。 我想要了解的是本机代码如何处理exception。

抖动比“反汇编”窗口中显示的内容要多得多。 例如,它生成一个表,描述局部变量的生命周期和存储。 对于垃圾收集器非常重要,它需要该表来查找对象引用。

它会为exception生成一个展开表。 它有一个非常理想的属性,它免费提供一个try语句。 编写具有exception处理的代码没有成本,输入try块不需要代码。 所以你在反汇编中没有看到任何东西。 没有简单的方法可以从调试器中找到该表。 这里有一个相当不错的描述。

实际上,当没有抛出exception时,您实际上正在查看exception处理在.NET中的开销很小的原因。 在发生exception的情况下,框架使用正常代码流路径之外的各种算法(具体的算法取决于抛出的exception类型),它将调用或显式设置指向该方法的exception处理块的指令指针。