CLR为什么使用两次通过异常模型?

问题描述 投票:-2回答:1

[Depth,4th Edition中C#中有一章与C#6中添加的异常过滤器有关。它描述了CLR的异常处理模型:

您可能已经习惯了CLR取消堆栈的想法,因为异常“冒泡”直到被捕获。更令人惊讶的是这是如何发生的。该过程比使用两次通过模型所预期的要复杂。该模型使用以下步骤:

  • 引发异常,并且首次通过开始。
  • CLR沿着栈走,试图找到哪个catch块将处理异常。 (我们将其简称为handle catch block,但这不是官方术语。)
  • 仅考虑具有兼容异常类型的catch块。
  • 如果catch块具有异常过滤器,则执行该过滤器;如果过滤器返回false,则此catch块不会处理异常。
  • 没有异常过滤器的catch块等效于带有返回true的异常过滤器的catch块。
  • 现在已经确定了处理捕获块,第二遍开始:
  • CLR从引发异常的位置开始将堆栈展开至已确定的catch块。
  • 执行所有最终在展开堆栈时遇到的块。 (这不包括任何与处理catch块相关联的finally块。)
  • 执行处理捕获块。
  • 如果存在,则执行与处理catch块关联的finally语句。

[从C#开发人员的角度来看,两次处理语义之间的唯一区别与处理所有内容的一次处理相对,似乎是异常过滤器与finally块执行的顺序-在两次处理语义中,所有过滤器都是在任何finally块之前进行求值。我思考得越多,就越看不出为什么这样决定。

  1. 单次通过听起来更有效,所以我认为这是默认的实现方式,除非有足够的理由再选择一次。
  2. 在选择了处理块之后,
  3. 执行finally子句可能会使控制流在使过滤谓词无效之后进入一个块。显示此内容的简单示例:
public class C
{
    private static int State = 42;

    public static void Foo()
    {
        try
        {
            throw new Exception();
        }
        finally
        {
            State = 17;
        }
    }

    public static void Main()
    {
        try
        {
            Foo();
        }
        catch (Exception) when (State == 42)
        {
            Console.WriteLine(State);
        }
    }
}
> 17

这是违反直觉的,因为为catch块编写代码时,很容易将代码读取为“当我进入catch块时State始终为42”,而实际的语义是“当我输入catch块,我知道当引发捕获的异常时,State42”。因此,该行的文字读取不起作用-它是notcatchExceptionwhenState等于42”,如果是catch,则为[Exception when抛出State等于42“。

  1. 您可以“反转”该参数并制作一个finally块,该块将使较低的滤波器适用。因此,如果我们将第2点的代码修改为when (State == 17),则不会捕获该异常并使应用程序崩溃,即使直觉上人们会认为它“通过”了catchwhen State等于17

  2. 此模型的一个优势是所有finally块均独立于过滤谓词。因此,如果过滤器有任何副作用,我们可以保证它们将在运行任何finally块之前自底向上依次运行。但这是有争议的,因为带有副作用的过滤器似乎是对系统的滥用,而finally块是通常需要施加某种副作用进行清理的普通代码。

列出以上所有内容使我认为其中之一:

  • 我看不出该模型比“轻松”的一遍模型有什么好处;或

  • 有一个与深层次技术有关的深层技术原因,这就是为什么CLR仅以这种方式才能正常工作。

  • 我想知道它是哪一个,如果是深层次的技术原因,请给我一个“傻瓜”摘要。乔恩·斯凯特(Jon Skeet)在该章中指出:“ [此模型的起源]比我想冒险的要深入CLR,因此我希望这个原因是技术性的并且不是显而易见的,但是听到来自以下方面的推理将是一件很高兴的事是CLR基础架构专家的人。

[Depth,4th Edition中C#中有一章与C#6中添加的异常过滤器有关。它描述了CLR的异常处理模型:您可能已经习惯了CLR解散...的概念]]

c# exception clr
1个回答
0
投票
尽管没有设置工具来利用两遍设计的所有潜在优势,但是在许多情况下,发生异常的地方的代码无法立即知道是否会发生异常。在调试器中检查(或以其他方式记录)在执行到达异常处理程序之前将被“清理”的系统状态的各个方面很有用。

如果是例如在某些预期代码可能会失败(并会捕获异常)的情况下,从某些远程上下文中读取远程网站上的数据,在某些意外失败的情况下,可以怀疑执行到在后一种情况下,如果抛出了异常,请检查套接字的状态,而在预期失败的情况下不必暂停执行。

© www.soinside.com 2019 - 2024. All rights reserved.