【问题标题】:C# exception handling finally block before catch blockC# 异常处理 finally 块在 catch 块之前
【发布时间】:2019-03-31 08:13:54
【问题描述】:

我对 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();
    }
}

【问题讨论】:

  • 最终将始终独立执行,您是否处理异常。因此,如果异常是 IndexOutOfRangeException 类型,您将始终在控制台中看到 AB
  • Specification:一旦找到匹配的catch 子句,系统就准备将控制权转移到catch 子句的第一条语句。在开始执行catch 子句之前,系统首先按顺序执行与try 语句关联的任何finally 子句,其嵌套程度比捕获异常的语句的嵌套程度高。
  • 您的编辑与我回答的后半部分有关,其中根本找不到异常处理程序。
  • 似乎有人对每个答案都投了反对票。留下评论来解释原因会很有帮助
  • However, my finally block never gets executed and in return I am getting a run time error 这不是真的。它们被执行并且你在运行时得到未处理的异常错误。见这里:Test。可以看到A最后打印出来然后异常。

标签: c# exception-handling


【解决方案1】:

finally 在控制离开它所附加的try 块时执行出于任何原因;不仅仅是在同一级别有一个 catch 块 - 那将是一个 fault 处理程序(在 CLR 术语中),我认为我们仍然无法在 C# 中执行。

所以它进入了打印Afinally,因为控制离开了try 块。它离开它是因为外部catch 块中捕获的异常。但是,这不会改变任何时间/顺序。


请注意,如果异常在您的代码中的任何地方都完全未被捕获,则可能会出现一些奇怪的情况。异常处理分两个阶段进行。在第一阶段,它试图为异常找到合适的catch 子句,这可能包括执行保护子句(when,C#6 和更高版本)。在第二阶段,它展开堆栈并根据嵌套需要执行任何 finally 子句,然后到达定义将处理异常的 catch 子句的正确级别1

我相信在某些环境中,如果第一阶段未能找到适当的异常处理程序(catch 子句)根本,整个托管环境可能会被拆除。在这种情况下,finally 子句不会被执行。

符合 CLR 标准的所有要求都在 MS Partition I.pdf 文档中进行了描述,该文档位于 ECMA C# and Common Language Infrastructure Standards。第 12.4.2.5 节指出:

发生异常时,CLI 在数组中搜索第一个受保护的块

  • 保护包含当前指令指针的区域

  • 是一个catch handler块

  • 谁的过滤器希望处理异常

如果在当前方法中没有找到匹配项,则搜索调用方法,依此类推。 如果没有找到匹配项 CLI 将转储堆栈跟踪并中止程序。

(我的强调


1使用 Flydog57's example 的变体进行说明,也是 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

【讨论】:

    【解决方案2】:

    在您的第一个代码中,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 代码就会执行。然后轮到外面的 catch 块了。

    【讨论】:

      【解决方案3】:

      我可能遗漏了什么。您不想要以下内容吗?

      try
      {          
      }
      catch (Exception ex)
      {
      }
      finally
      {  
      }
      

      通过使用finally{} 块,您可以确保某些语句将始终运行

      【讨论】:

      • 我实际上是在尝试理解一个 mcq 问题,这就是格式一开始就搞砸的原因。但是“通过使用 finally{} 块,您可以确保某些语句将始终运行”不起作用。请参阅我的编辑部分。谢谢
      【解决方案4】:

      它应该看起来像这样(取决于你实际想要做什么)

      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();
      }
      }
      

      【讨论】:

      • 我正在尝试回答一个 mcq 问题
      • @Svenmarim 不,你没有。
      • 这不是真的!您可以使用 try-catch-finally、try-catch、try-finally 变体。
      【解决方案5】:

      如果你有一个 try-catch-finally 块,那么 catch 确实在 finally 之前。但是在这里,try-finally 和 try-catch 块是按照从内到外的顺序运行的。因此 finally 先运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-26
        • 2016-09-19
        • 1970-01-01
        • 2019-01-07
        相关资源
        最近更新 更多