[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
块之前进行求值。我思考得越多,就越看不出为什么这样决定。
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
块,我知道当引发捕获的异常时,State
为42
”。因此,该行的文字读取不起作用-它是not“catch
Exception
when
State
等于42
”,如果是catch
,则为[Exception
when
抛出State
等于42
“。
您可以“反转”该参数并制作一个finally
块,该块将使较低的滤波器适用。因此,如果我们将第2点的代码修改为when (State == 17)
,则不会捕获该异常并使应用程序崩溃,即使直觉上人们会认为它“通过”了catch
块when
State
等于17
。
此模型的一个优势是所有finally
块均独立于过滤谓词。因此,如果过滤器有任何副作用,我们可以保证它们将在运行任何finally
块之前自底向上依次运行。但这是有争议的,因为带有副作用的过滤器似乎是对系统的滥用,而finally
块是通常需要施加某种副作用进行清理的普通代码。
列出以上所有内容使我认为其中之一:
我看不出该模型比“轻松”的一遍模型有什么好处;或
有一个与深层次技术有关的深层技术原因,这就是为什么CLR仅以这种方式才能正常工作。
[Depth,4th Edition中C#中有一章与C#6中添加的异常过滤器有关。它描述了CLR的异常处理模型:您可能已经习惯了CLR解散...的概念]]
如果是例如在某些预期代码可能会失败(并会捕获异常)的情况下,从某些远程上下文中读取远程网站上的数据,在某些意外失败的情况下,可以怀疑执行到在后一种情况下,如果抛出了异常,请检查套接字的状态,而在预期失败的情况下不必暂停执行。