简体   繁体   English

C#异常处理最后在catch块之前阻塞

[英]C# exception handling finally block before catch block

I am quite confused with how exceptions are getting thrown in C#. 我对如何在C#中抛出异常感到困惑。 If an exception occurs, in the try block, 1.it gets thrown to the catch block, 2. If and only if the catch block catches it the finally block will be executed. 如果发生异常,则在try块中,1.it被抛出到catch块,2。当且仅当catch块捕获它时,将执行finally块。 3. The finally block gets executed last, provided the catch statement caught it. 3.如果catch语句捕获了finally块,则finally块最后执行。

However, when I try to run the program below, the output is A,B not BA.Is there something wrong with my understanding? 但是,当我尝试运行下面的程序时,输出是A,B而不是BA。我的理解是否有问题? Thank you. 谢谢。

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

The exception occurs in a==2, and I know the outer catch will catch this exception. 异常发生在== 2中,我知道外部catch会捕获此异常。 However, the finally is being executed first? 但是,最终还是先执行了? Any reason as to why this is showing? 有什么理由说明这一点?

edited 编辑

From C# docs we know Finally block gets executed whether or not an exception has occured. 从C#docs我们知道,无论是否发生异常,都会执行finally块。

However, my finally block never gets executed and in return I am getting a run time error 但是,我的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 executes when control leaves the try block to which it is attached for any reason ; 当控制因任何原因离开它所连接的try块时, finally执行; not just if there was a catch block at the same level - that would be a fault handler (in CLR terms) which I think we still cannot do in C#. 不仅仅是在同一级别存在一个catch块 - 这将是一个fault处理程序(在CLR术语中),我认为我们仍然无法在C#中做到这一点。

So it enters the finally that prints A because control is leaving that try block. 所以它进入finally打印A因为控件正在离开那个try块。 It's leaving it because of an exception that's being caught in an outer catch block. 由于异常被捕获在外部catch块中而离开它。 That doesn't change any timings/orderings however. 但是,这并没有改变任何时间/顺序。


Note that there are some oddities that are possible if the exception is entirely uncaught anywhere in your code. 请注意,如果异常在代码中的任何位置完全未被捕获,则可能存在一些奇怪之处。 Exception handling happens in two phases. 异常处理分两个阶段进行。 In the first phase, it's trying to locate an appropriate catch clause for the exception, and that can include executing guard clauses ( when , C#6 and later). 在第一阶段,它试图为异常找到一个合适的catch子句,并且可以包括执行guard子句( when ,C#6和更高版本)。 During the second phase, it unwinds the stack and executes any finally clauses as the nesting requires, before reaching the correct level at which the catch clause that will handle the exception is defined 1 . 在第二阶段,它将展开堆栈并执行任何finally子句,因为嵌套需要,然后达到正确的级别,在该级别定义将处理异常的catch子句1

I believe that in some environments, if the first phase fails to locate an appropriate exception handler ( catch clause) at all , the entire managed environment may be torn down. 我相信,在某些环境中,如果第一阶段未能找到一个合适的异常处理程序( catch 所有条款),整个管理的环境,可以拆除。 In such circumstances, the finally clauses aren't executed. 在这种情况下, finally子句不会执行。

All that is required by a standards conforming CLR is described in the MS Partition I.pdf document, found on ECMA C# and Common Language Infrastructure Standards . 所有这一切都是由符合CLR的MS分区I.pdf文件,就发现中描述的标准所要求的 ECMA C#和通用语言基础设施标准 Section 12.4.2.5 states: 第12.4.2.5节规定:

When an exception occurs, the CLI searches the array for the first protected block that 发生异常时,CLI将在阵列中搜索第一个受保护的块

  • Protects a region including the current instruction pointer and 保护包含当前指令指针区域的区域

  • Is a catch handler block and 是一个catch处理程序块

  • 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. 如果未找到匹配项,CLI将转储堆栈跟踪并中止该程序。

(My emphasis ) (我的重点


1 Illustrated using a variant of Flydog57's example , also C# 7 local functions: 1使用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;
            }
        }
    }
}

This prints: 这打印:

Start Outer Try
Start Inner Try
In outer guard
In Inner Finally
In outer catch
In outer finally
Complete

Illustrating the two-pass nature of the exception handling (that In outer guard is printed before In Inner Finally ) 说明异常处理的两次通过性质( In Inner Finally In outer guard In Inner Finally 之前打印)

If you have a single try-catch-finally block, it's true that catch precedes finally. 如果你有一个try-catch-finally块,那么catch最终会成功。 But here, the try-finally and try-catch blocks are being run in order of innermost to outermost. 但是在这里,try-finally和try-catch块按照从最里面到最外面的顺序运行。 Therefore the finally runs first. 因此终于先行了。

It should look something like this (depents on what you are actually trying to do) 它应该看起来像这样(取决于你实际上想要做什么)

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

In your first code, the reason that the finally (A) runs before the catch (B) is because when the exception is thrown, you exit the inner block (causing the finally to run) before the outer block's catch comes into play. 在你的第一个代码中, finally (A)在catch (B)之前运行的原因是因为抛出异常时,在外部块的catch进入游戏之前退出内部块(导致finally运行)。 Consider this code: 考虑以下代码:

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

If I run this code, I get this output: 如果我运行此代码,我得到此输出:

Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In inner catch
In Inner Finally
In outer finally

Which is what you expect. 这是你所期望的。 But, if I remove the inner catch block (as noted in the code), I get this output: 但是,如果我删除内部catch块(如代码中所述),我得到这个输出:

Start Outer Try
Start Inner Try
Exception thrown: 'System.Exception' in MyTestApp.exe
In Inner Finally
In outer catch
In outer finally

In this case, as soon as execution exits the inner try block, the finally code executes. 在这种情况下,只要执行退出内部try块,finally代码就会执行。 Then the outer catch block has it's turn. 然后外部挡块再转动。

I may be missing something. 我可能会遗漏一些东西。 Don't you want the following? 你不想要以下吗?

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

By using a finally{} block, you can ensure that some statements will always run 通过使用finally{}块,您可以确保始终运行某些语句

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM