Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在finally块中将未处理的异常更改为已处理的异常_C#_.net_Exception Handling_Language Lawyer - Fatal编程技术网

C# 在finally块中将未处理的异常更改为已处理的异常

C# 在finally块中将未处理的异常更改为已处理的异常,c#,.net,exception-handling,language-lawyer,C#,.net,Exception Handling,Language Lawyer,考虑一下这个计划: using System; static class Program { static void Main(string[] args) { try { try { throw new A(); } finally { throw new B(); } } catch (B) { } Console.WriteLine("All done!"); } } class A : Exception { } cla

考虑一下这个计划:

using System;
static class Program {
  static void Main(string[] args) {
    try {
      try { throw new A(); }
      finally { throw new B(); }
    }
    catch (B) { }
    Console.WriteLine("All done!");
  }
}

class A : Exception { }

class B : Exception { }
这里,抛出类型为
A
的异常,该异常没有处理程序。在
finally
块中,抛出类型为
B
的异常,该异常有一个处理程序。通常,在
块中抛出的异常最终会赢,但对于未处理的异常则不同

调试时,当抛出
A
时,调试器停止执行,并且不允许执行
finally

当不调试(从命令提示符独立运行)时,会显示一条关于未处理异常的消息(打印,并显示一个崩溃对话框),但在此之后,“全部完成!”会打印出来

当添加一个顶级异常处理程序时,它只会重新显示捕获的异常,一切正常:没有意外消息,并且会打印“全部完成!”

我理解这是如何发生的:在执行任何
最终
块之前,确定异常是否有处理程序。这通常是可取的,而当前的行为是有道理的<代码>最后
块通常不会抛出异常

但是引用了C语言规范并声称需要
finally
块来覆盖
A
异常。阅读本规范后,我同意这正是它所要求的:

  • 在当前函数成员中,将检查包含抛出点的每个
    try
    语句。对于从最内层的try语句开始并以最外层的try语句结束的每条语句,将计算以下步骤:
    • 如果
      S
      try
      块包含抛出点,并且如果S有一个或多个
      catch
      子句,则将检查catch子句[…]
    • 否则,如果
      try
      块或
      S
      catch
      块包含抛出点,并且如果
      S
      具有
      finally
      块,则控制权转移到
      finally
      块。如果
      finally
      块抛出另一个异常,则终止当前异常的处理。否则,当控件到达
      finally
      块的终点时,将继续处理当前异常
  • 如果异常处理程序不在当前函数调用中,则函数调用将终止,并发生以下情况之一:
    • [……]
  • 如果异常处理终止当前线程中的所有函数成员调用,表明该线程没有异常处理程序,则该线程本身终止。此类终止的影响由实施定义
根据我对规范的阅读,除非所有函数调用都已终止,否则不会认为异常未处理,函数调用直到
最终执行
处理程序后才会终止


我是否在这里遗漏了什么,或者微软的C#实现与他们自己的规范不一致?

我认为问题在于.NET异常处理是如何建立在结构化异常处理之上的,结构化异常处理在抛出finally块时有着稍微不同的规则

当异常发生时,SEH尝试找到第一个能够处理您的异常类型的处理程序,然后开始运行所有finally块,并将其解绑到它,但基于SEH逻辑,没有这样的处理程序,因此它会在.NET可以强制执行其自己的规则之前发出未处理异常的警报

这解释了解决问题的顶级处理程序(但仅限于能够处理异常类型A的处理程序)

IL本身看起来是有效的:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       49 (0x31)
  .maxstack  1
  IL_0000:  nop
  IL_0001:  nop
  .try
  {
    IL_0002:  nop
    .try
    {
      IL_0003:  nop
      IL_0004:  newobj     instance void A::.ctor()
      IL_0009:  throw
    }  // end .try
    finally
    {
      IL_000a:  nop
      IL_000b:  newobj     instance void B::.ctor()
      IL_0010:  throw
    }  // end handler
  }  // end .try
  catch B 
  {
    IL_0011:  pop
    IL_0012:  nop
    IL_0013:  ldstr      "B"
    IL_0018:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_001d:  nop
    IL_001e:  nop
    IL_001f:  leave.s    IL_0021
  }  // end handler
  IL_0021:  nop
  IL_0022:  nop
  IL_0023:  nop
  IL_0024:  ldstr      "A"
  IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_002e:  nop
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::Main

Mono也有同样的问题

Pavel Krymets的答案表明C编译器相当直接地将
try
/
catch
/
最终
转换为CIL
。try
/
catch
/
最终
,Hans Passant对我的问题的评论指出了CIL规范要求当前行为的地方。就问题而言,这确实是C#编译器和C#规范之间的冲突

我注意到Roslyn编译器包含了实验性的新语言功能,其中一个新的语言功能涉及
try
/
catch
:它支持异常过滤器,使用
try
/
catch if
语法:

try {
  ...
}
catch (Exception e) if (...) {
  ...
}
异常过滤器的一个要点是,它们在任何
finally
块之前运行,以确定异常是否有任何处理程序。语言规范尚未更新以涵盖这一点:Visual Studio 2015预览版中包含的语言规范是旧的C#5.0语言规范。然而,如果不是完全不可能指定异常的行为,那么就很难声称在异常被认为未处理之前执行了
最后
块。考虑到这一点,我想说,可以相当肯定的是,不仅当前的行为是故意的,还可以相当肯定的是,规范将被更新以匹配


我接受Pavel Krymets的答案,因为尽管它本身并不能完全回答我的问题,但这是迈向完整答案的最大一步。

您是否尝试过将代码作为独立控制台应用程序运行?在我的计算机上,它打印“未处理的异常:A:抛出了类型为“A”的异常”。这肯定不是预期的行为。不,我得到了Windows错误报告对话框。如果我说取消,“全部完成!”就会打印出来。您使用的是哪个版本的.Net Windows?IL看起来像是C#到CIL的简单翻译,但是如果
的语义尝试使用C#