我对 C# 中如何抛出异常感到非常困惑。如果发生异常,在try块中, 1.它被扔到catch块中, 2. 当且仅当catch 块捕获它时,finally 块才会被执行。 3.finally 块最后执行,前提是 catch 语句捕获了它。
但是,当我尝试运行下面的程序时,输出是A,B而不是BA。我的理解有问题吗?谢谢你。
class Program
{
public static void Main(string[] args)
{
try
{
int a = 2;
int b = 10 / a;
try
{
if (a == 1)
a = a / a - a;
if (a == 2)
{
int[] c = { 1 };
c[8] = 9;
}
}
finally
{
Console.WriteLine("A");
}
}
catch (IndexOutOfRangeException e)
{
Console.WriteLine("B");
}
Console.ReadLine();
}
}
异常发生在a==2,我知道外层catch会捕获这个异常。然而finally是先被执行的吗?有什么原因可以解释为什么会出现这种情况吗?
从 C# 文档中我们知道无论是否发生异常,Finally 块都会执行。
但是,我的finally 块永远不会被执行,作为回报,我收到运行时错误
class Program
{
public static void Main(string[] args)
{
try
{
int a = 2;
int b = 10 / a;
try
{
if (a == 1)
a = a / a - a;
if (a == 2)
{
int[] c = { 1 };
c[8] = 9;
}
}
finally
{
Console.WriteLine("A");
}
}
finally{
Console.WriteLine("finally");
}
Console.ReadLine();
}
}
离开其所附着的
finally
块时,try
执行;不仅仅是同一级别有一个 catch
块 - 那将是一个
fault
处理程序(用 CLR 术语来说),我认为我们在 C# 中仍然无法做到这一点。因此它进入打印
finally
的
A
,因为控制正在离开该
try
块。它之所以离开它,是因为在外部
catch
块中捕获了异常。但这不会改变任何时间/顺序。
catch
子句,其中可以包括执行保护子句(
when
、C#6 及更高版本)。在第二阶段,它会展开堆栈并根据嵌套需要执行任何 finally
子句,然后到达定义处理异常的
catch
子句的正确级别1。 我相信,在某些环境中,如果第一阶段根本无法找到合适的异常处理程序(
catch
子句),整个托管环境可能会被拆除。在这种情况下,
finally
子句不会被执行。符合 CLR 标准的所有要求
都在 MS Partition I.pdf 文档中进行了描述,该文档可以在ECMA C# 和公共语言基础设施标准上找到。第 12.4.2.5 节规定:
当发生异常时,CLI 在数组中搜索第一个受保护的块强调
保护包括当前指令指针的区域
如果没有找到匹配项 CLI 将转储堆栈跟踪并中止程序。- 和
是一个catch处理程序块
- 和
谁的过滤器希望处理异常
- 如果在当前方法中没有找到匹配的,则查找调用方法,以此类推。
(我的
)
使用 Flydog57 示例的变体进行说明,也是 C# 7 本地函数:
using System;
namespace PlayAreaCSCon
{
internal class Program
{
static void Main(string[] args)
{
TestTryCatchFinally();
Console.WriteLine("Complete");
Console.ReadLine();
}
private static void TestTryCatchFinally()
{
try
{
Console.WriteLine("Start Outer Try");
try
{
Console.WriteLine("Start Inner Try");
throw new Exception("Exception from inner try");
}
finally
{
Console.WriteLine("In Inner Finally");
}
}
catch (Exception ex) when (GuardHelper(ex))
{
Console.WriteLine("In outer catch");
}
finally
{
Console.WriteLine("In outer finally");
}
bool GuardHelper(Exception ex)
{
Console.WriteLine("In outer guard");
return true;
}
}
}
}
打印:
Start Outer Try
Start Inner Try
In outer guard
In Inner Finally
In outer catch
In outer finally
Complete
说明异常处理的两遍性质(
In outer guard
打印于
before
In Inner Finally
)finally
(A)在
catch
(B)之前运行的原因是,当抛出异常时,您在外部块之前退出内部块(导致finally
运行)块的 catch
开始发挥作用。 考虑这段代码:private void TestTryCatchFinally()
{
try
{
Debug.WriteLine("Start Outer Try");
try
{
Debug.WriteLine("Start Inner Try");
throw new Exception("Exception from inner try");
Debug.WriteLine("End of Inner Try - never reaced");
}
//remove this catch block for second test
catch (Exception)
{
Debug.WriteLine("In inner catch");
}
//end of code to remove
finally
{
Debug.WriteLine("In Inner Finally");
}
}
catch (Exception)
{
Debug.WriteLine("In outer catch");
}
finally
{
Debug.WriteLine("In outer finally");
}
}
如果我运行此代码,我会得到以下输出:
Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In inner catch
In Inner Finally
In outer finally
这正是您所期望的。 但是,如果我删除内部 catch 块(如代码中所述),我会得到以下输出:
Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In Inner Finally
In outer catch
In outer finally
在这种情况下,一旦执行退出内部 try 块,finally 代码就会执行。 然后轮到外部捕获块了。
class Program
{
public static void Main(string[] args)
{
try
{
int a = 2;
int b = 10 / a;
if (a == 1)
a = a / a - a;
if (a == 2)
{
int[] c = { 1 };
c[8] = 9;
}
}
catch (IndexOutOfRangeException e)
{
//Do something when something in try block throws error
Console.WriteLine("B");
}
finally
{
//This code will ALWAYS execute
//Even when there is no error, A will be written to console
Console.WriteLine("A");
}
Console.ReadLine();
}
}
请注意,异常一旦创建就存在,除非它被捕获并处理。如果代码没有捕获它 - 将会导致意外的结果或错误
try
{
}
catch (Exception ex)
{
}
finally
{
}
通过使用
finally{}
块,您可以确保某些语句将始终运行