C# 异常处理 finally 在 catch 块之前阻塞
C# exception handling finally block before catch block
我对 C# 中抛出异常的方式感到很困惑。如果发生异常,在try块中,
1.it 被扔到 catch 块,
2. 当且仅当 catch 块捕获到它时,finally 块才会被执行。
3. finally 块最后执行,前提是 catch 语句捕获了它。
但是,当我尝试 运行 下面的程序时,输出的是 A,B 而不是 BA.Is 我的理解有问题吗?谢谢。
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 块永远不会执行,在 return 中,我收到 运行 时间错误
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();
}
}
如果您只有一个 try-catch-finally 块,那么 catch 确实先于 finally。但是在这里,try-finally 和 try-catch 块按照从内到外的顺序 运行。因此最后 运行 是第一个。
finally
在控制离开 try
块时执行 出于任何原因;不仅仅是在同一级别有一个 catch
块 - 那将是一个 fault
处理程序(在 CLR 术语中),我认为我们在 C# 中仍然无法做到。
因此它进入打印 A
的 finally
,因为控制正在离开那个 try
块。它离开它是因为在外部 catch
块中捕获了一个异常。但是,这不会改变任何 timings/orderings。
请注意,如果异常在您的代码中的任何地方都完全未被捕获,则可能会出现一些奇怪的情况。异常处理分两个阶段进行。在第一阶段,它试图为异常找到一个合适的 catch
子句,这可能包括执行保护子句(when
、C#6 及更高版本)。在第二阶段,它展开堆栈并根据嵌套要求执行任何 finally
子句,然后到达定义将处理异常的 catch
子句的正确级别1 .
我相信在某些环境中,如果第一阶段无法找到合适的异常处理程序(catch
子句)根本,整个托管环境可能拆了。在这种情况下,不会执行 finally
子句。
符合标准的 CLR 要求 的所有内容都在 ECMA C# and Common Language Infrastructure Standards 上的 MS 分区 I.pdf 文档中进行了描述。第 12.4.2.5 节指出:
When an exception occurs, the CLI
searches the array for the first protected block that
Protects a region including the current instruction pointer and
Is a catch handler block and
Whose filter wishes to handle the exception
If a match is not found in the current method, the calling method is searched, and so on. If no match is found
the CLI will dump a stack trace and abort the program.
(我的重点)
1使用 的变体进行说明,还有 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
在 之前 In Inner Finally
打印 In Inner 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{}
块,您可以确保某些语句始终 运行
在你的第一个代码中,finally
(A) 运行s 在 catch
(B) 之前的原因是因为抛出异常时,你退出了内部在外部块的 catch
发挥作用之前块(导致 finally
到 运行)。考虑这段代码:
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 代码就会执行。然后轮到外面的 catch 块了。
我对 C# 中抛出异常的方式感到很困惑。如果发生异常,在try块中, 1.it 被扔到 catch 块, 2. 当且仅当 catch 块捕获到它时,finally 块才会被执行。 3. finally 块最后执行,前提是 catch 语句捕获了它。
但是,当我尝试 运行 下面的程序时,输出的是 A,B 而不是 BA.Is 我的理解有问题吗?谢谢。
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 块永远不会执行,在 return 中,我收到 运行 时间错误
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();
}
}
如果您只有一个 try-catch-finally 块,那么 catch 确实先于 finally。但是在这里,try-finally 和 try-catch 块按照从内到外的顺序 运行。因此最后 运行 是第一个。
finally
在控制离开 try
块时执行 出于任何原因;不仅仅是在同一级别有一个 catch
块 - 那将是一个 fault
处理程序(在 CLR 术语中),我认为我们在 C# 中仍然无法做到。
因此它进入打印 A
的 finally
,因为控制正在离开那个 try
块。它离开它是因为在外部 catch
块中捕获了一个异常。但是,这不会改变任何 timings/orderings。
请注意,如果异常在您的代码中的任何地方都完全未被捕获,则可能会出现一些奇怪的情况。异常处理分两个阶段进行。在第一阶段,它试图为异常找到一个合适的 catch
子句,这可能包括执行保护子句(when
、C#6 及更高版本)。在第二阶段,它展开堆栈并根据嵌套要求执行任何 finally
子句,然后到达定义将处理异常的 catch
子句的正确级别1 .
我相信在某些环境中,如果第一阶段无法找到合适的异常处理程序(catch
子句)根本,整个托管环境可能拆了。在这种情况下,不会执行 finally
子句。
符合标准的 CLR 要求 的所有内容都在 ECMA C# and Common Language Infrastructure Standards 上的 MS 分区 I.pdf 文档中进行了描述。第 12.4.2.5 节指出:
When an exception occurs, the CLI searches the array for the first protected block that
Protects a region including the current instruction pointer and
Is a catch handler block and
Whose filter wishes to handle the exception
If a match is not found in the current method, the calling method is searched, and so on. If no match is found the CLI will dump a stack trace and abort the program.
(我的重点)
1使用
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
在 之前 In Inner Finally
打印 In Inner 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{}
块,您可以确保某些语句始终 运行
在你的第一个代码中,finally
(A) 运行s 在 catch
(B) 之前的原因是因为抛出异常时,你退出了内部在外部块的 catch
发挥作用之前块(导致 finally
到 运行)。考虑这段代码:
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 代码就会执行。然后轮到外面的 catch 块了。