【问题标题】:How does the catch keyword determine the type of exception that was thrown?catch 关键字如何确定抛出的异常类型?
【发布时间】:2012-08-30 14:54:06
【问题描述】:

catch 关键字如何确定抛出的异常类型?选择执行哪个 catch 块会发生什么过程?

try
{
    int[] myArray = new int[0];
    myArray[1] = 0;
}
catch (IndexOutOfRangeException ex) { } // how does the CLR know to enter here?
catch (InvalidCastException ex) { }

通过 ILdasm

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       28 (0x1c)
  .maxstack  3
  .locals init (int32[] V_0,
           class [mscorlib]System.IndexOutOfRangeException V_1,
           class [mscorlib]System.InvalidCastException V_2)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldc.i4.0
    IL_0003:  newarr     [mscorlib]System.Int32
    IL_0008:  stloc.0
    IL_0009:  ldloc.0
    IL_000a:  ldc.i4.1
    IL_000b:  ldc.i4.0
    IL_000c:  stelem.i4
    IL_000d:  nop
    IL_000e:  leave.s    IL_001a
  }  // end .try
  catch [mscorlib]System.IndexOutOfRangeException 
  {
    IL_0010:  stloc.1
    IL_0011:  nop
    IL_0012:  nop
    IL_0013:  leave.s    IL_001a
  }  // end handler
  catch [mscorlib]System.InvalidCastException 
  {
    IL_0015:  stloc.2
    IL_0016:  nop
    IL_0017:  nop
    IL_0018:  leave.s    IL_001a
  }  // end handler
  IL_001a:  nop
  IL_001b:  ret
 } // end of method Program::Main

但仍不清楚catch 关键字用于确定引发的异常类型的作用。

【问题讨论】:

  • AFAIK,使用其特定类型引发异常。然后,对于每个 catch(Type e) 块,它将类似于 e is Type
  • @AndreCalil:就我而言,您可以将其发布为答案。
  • 您是否尝试过查看 CLR\C# 规范?
  • 我比较好奇代码长什么样子。具体来说,编译器将如何实现这一点?
  • @frinkenstein789 它会像类型测试一样被编译,例如obj is Type

标签: c# .net exception try-catch


【解决方案1】:

免责声明:我没有阅读 CLR 规范,我知道异常比“正常”代码复杂得多。但是,这是一种非常简单的方法,我相信它会奏效。

  1. 当抛出异常时,生成它的代码将使用特定类型,例如IndexOutOfBoundsHttpException,而不是基类Exception
  2. 当在try 块内捕获异常时,运行时将有一个预期类型的​​异常列表(即,每个catch 块对应一个)。假设它是List<Type> TheseExceptionsMayOccur
  3. 最后会是这样的

.

foreach(Type ExceptionType in TheseExceptionMayOccur)
{
    if(e is ExceptionType )
        //run the respective catch block
}

【讨论】:

    【解决方案2】:

    只是一个简短的答案,真正的答案需要一本书。 .NET 中的异常处理异常复杂,涉及许多活动部件。包括对 Windows 中异常的本机支持(SEH,结构化异常处理),CLR 中最大和最复杂的代码块之一(excep.cpp,232KB 代码),程序集中的元数据(这就是你为什么不这样做的原因)看不到它占用任何 IL 地址)和抖动。

    throw 关键字引发异常,它在运行时触发 RaiseException() Windows api 函数。 Windows 通过运行由 RtlAddFunctionTable() 注册的异常过滤器来寻找愿意处理异常的代码。哪些是在 CLR 中实现的。它反过来使用由抖动生成的元数据,即当抖动将 IL 转换为机器代码时在即时编译时构建的数据表。抖动使用通过 .try 和 catch 等指令添加到程序集元数据的元数据信息,您在反汇编中看到的内容。表数据包含有关代表特定异常类型的 catch 子句的代码范围的信息。允许 CLR 选择应恢复执行的位置并告诉 Windows 继续处理异常。

    知道它从哪里开始并知道在哪里停止,Windows 现在开始展开堆栈帧,并在必要时调用 finally 块。接下来,它在 catch 块的第一条机器代码指令处设置的指令指针。

    除此之外还有很多很多令人讨厌的小细节。与 ThreadAbortException 的语义一样,vb.net Catch When 关键字、finally 阻塞引发异常的复杂性、SEH 纯粹基于堆栈帧时处理 try 阻塞范围、线程需要中止时无法捕获的异常的概念。需要一本书但没人会写的东西,因为读者会立即入睡。

    .NET 中的异常处理是一座充满铅的冰山。 99% 都在水下,CLR 中最成功的抽象之一。除了程序员经常使用 catch 关键字之外,这与它的实现方式没有任何关系。

    【讨论】:

    • 需要一本书但没人会写的东西,因为读者会立即入睡。就像我在阅读您的答案时几乎一样。 :) 很好的答案,我不知道错误处理是如此复杂。谢谢!
    • 汉斯一如既往地感谢您的智慧。无论如何,我知道我的答案绝对简单 =)
    • 哇。不知道异常是如此复杂。关于这个问题,我认为当 Exeption 可以强制转换为在 catch 块中声明时,CLR 将使用 catch 块。在这种情况下,如果第一个 catch 块是一般的 catch(Exception e) 它总是会去那里而不阅读 other catch 块声明...但再次 - 很高兴知道 @HansPassant跨度>
    • 需要一本书但没人会写的东西,因为读者会立即入睡。这确实是一本好书;这对我来说将非常有用。只要我将这本书命名为“专家睡眠指南”而不是“.Net 中的异常处理”,作者就可以卖出数百万册:p
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 2012-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-16
    • 1970-01-01
    相关资源
    最近更新 更多