.net try catch和finally块的执行顺序

rdlzhqv9  于 2023-02-06  发布在  .NET
关注(0)|答案(5)|浏览(129)

我搞不清try、catch和finally block执行的顺序。
我还想知道什么时候我应该使用try-catch块,我应该在try-catch块中放入什么?我还想知道如果一些异常进入try块,那么如果采取了对应于try块的操作,那么哪个是先执行catch还是最终执行(总是要执行)?在这两个执行之后,控制返回到try块还是离开它?

c90pui9n

c90pui9n1#

如果您有(**注意:**这不是有效的C#,请参见下面的有效示例):

try {
   // ... some code: A
} catch(...) {
   // ... exception code: B
} finally {
   // finally code: C
}

代码A将被执行。如果一切顺利(即A执行时没有抛出异常),它将进入finally,因此代码C将被执行。如果A执行时抛出异常,则它将进入B,然后最终进入C。
例如,下面是http://msdn.microsoft.com/en-us/library/dszsf989.aspx中的一个有效C#代码块:

public class EHClass
{
    void ReadFile(int index)
    {
        // To run this code, substitute a valid path from your local machine
        string path = @"c:\users\public\test.txt";
        System.IO.StreamReader file = new System.IO.StreamReader(path);
        char[] buffer = new char[10];
        try
        {
            file.ReadBlock(buffer, index, buffer.Length);
        }
        catch (System.IO.IOException e)
        {
            Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
        }
        finally
        {
            if (file != null)
            {
                file.Close();
            }
        }
        // Do something with buffer...
    }
}

使用try/catch/finally的原因是为了防止程序在某些代码出错时失败(上例中的A)。如果出现问题,您可以使用catch部分来捕获问题并执行一些有用的操作,例如通知用户,将异常记录到日志文件,重试或尝试其他您认为可能有效的操作,而不是最初尝试的操作。
finally用于确保执行某些清理。例如,在A中,您可能尝试打开并读取一个文件。如果打开成功,但读取失败,您将有一个打开的文件悬空。在这种情况下,您希望将其关闭,这将在finally块中执行-此块始终得到执行,以确保文件的关闭。
查看此处了解更多信息:

kmpatx3s

kmpatx3s2#

try ... catch块用于捕获异常。在try块中放入预期可能引发异常的代码。
如果没有异常发生,那么try块中的代码按预期完成,如果有finally块,那么接下来将执行该块。
如果发生异常,则执行跳转到第一个匹配的catch块的开头。一旦该代码完成,则执行finally块(如果存在)。执行不会返回到try块。

jaql4c8m

jaql4c8m3#

您几乎不应该使用try/catch。
你应该只处理那些你可以纠正的catch异常,并且只在你期待它们的时候,否则,让调用者处理这个异常--或者不处理。
如果使用,则首先执行任何catch子句-仅执行其中一个子句。
然后,“最终”执行finally
这在很多地方都有更好的表述,但我还是会尝试。

try
{
    // Do something here
}
catch (Exception ex)
{
    MessageBox.Show("Friendly error message");
}

并不修复异常。它隐藏了异常,这样问题就永远不会被修复。该代码不知道抛出了哪个异常,因为它会捕获所有异常,而且它没有做任何事情来纠正问题-它只是告诉用户一个礼貌的虚构。
事实上,上述代码应替换为以下代码:

// Do something here

这样,如果此方法的调用方知道如何修复特定问题,则调用方可以修复这些问题。您将不会从调用方删除该选项。
如果调用方不知道如何修复问题,则调用方也不应捕获异常。
下面是一个以合理方式使用异常的示例(来自MSDN),它是SmtpFailedRecipientsException Class文档中示例的修改形式。

public static void RetryIfBusy(string server)
{
    MailAddress from = new MailAddress("ben@contoso.com");
    MailAddress to = new MailAddress("jane@contoso.com");
    using (
        MailMessage message = new MailMessage(from, to)
                                  {
                                      Subject = "Using the SmtpClient class.",
                                      Body =
                                          @"Using this feature, you can send an e-mail message from an application very easily."
                                  })
    {
        message.CC.Add(new MailAddress("Notifications@contoso.com"));
        using (SmtpClient client = new SmtpClient(server) {Credentials = CredentialCache.DefaultNetworkCredentials})
        {
            Console.WriteLine("Sending an e-mail message to {0} using the SMTP host {1}.", to.Address, client.Host);
            try
            {
                client.Send(message);
            }
            catch (SmtpFailedRecipientsException ex)
            {
                foreach (var t in ex.InnerExceptions)
                {
                    var status = t.StatusCode;
                    if (status == SmtpStatusCode.MailboxBusy || status == SmtpStatusCode.MailboxUnavailable)
                    {
                        Console.WriteLine("Delivery failed - retrying in 5 seconds.");
                        System.Threading.Thread.Sleep(5000); // Use better retry logic than this!
                        client.Send(message);
                    }
                    else
                    {
                        Console.WriteLine("Failed to deliver message to {0}", t.FailedRecipient);
                            // Do something better to log the exception
                    }
                }
            }
            catch (SmtpException ex)
            {
                // Here, if you know what to do about particular SMTP status codes,
                // you can look in ex.StatusCode to decide how to handle this exception
                // Otherwise, in here, you at least know there was an email problem
            }
            // Note that no other, less specific exceptions are caught here, since we don't know
            // what do do about them
        }
    }
}

请注意,这段代码使用try/catch来包围一小段代码。在try/catch块中,如果抛出SmtpException或SmtpFailedRecipientsException,我们知道该怎么做。例如,如果我们要捕获IOException,我们将不知道它的含义,也不知道该怎么做。不应该捕获任何实际上不知道如何更正的异常。除了可能向异常添加信息、记录它并重新抛出。

huus2vyu

huus2vyu4#

下面是一个例子:

try
{
    someFunctionThatWorks();

    functionThatThrowsAnException(); // As soon as this function throws an exception we are taken to the catch block

    anotherFunction();  // <-- This line will never get executed
}
catch(Exception e)
{
    // Here you can handle the exception, if you don't know how to handle it you should not be catching it
    // After this you will not be taken back to the try block, you will go right to the finally block
}
finally
{
    // Code here is always executed at the very end, regardless of whether an exception was thrown or not
}
62lalag4

62lalag45#

我想详细说明一下这一点,并使用场景扩展@icyrock.com答案,当您在catch块中重新抛出异常时,它将在执行堆栈的较低层处理...
我用下面的代码试了一下:

static void Main(string[] args)
{
    try
    {
        // pick one:
        // NormalExcecution();
        // TroubleExcecution();
    }
    catch
    {
        Console.WriteLine("block D");
    }
    
    Console.ReadKey();
}

private static void NormalExcecution()
{
    try
    {
        Console.WriteLine("block A");
    }
    catch (Exception)
    {
        Console.WriteLine("block B");
        throw;
    }
    finally
    {
        Console.WriteLine("block C");
    }
}

private static void TroubleExcecution()
{
    try
    {
        Console.WriteLine("block A");
        throw new Exception();
    }
    catch (Exception)
    {
        Console.WriteLine("block B");
        throw;
    }
    finally
    {
        Console.WriteLine("block C");
    }
}

因此,当块A中没有异常时,序列如下(异常处理块永远不会命中):

Block A
Block C

当A块出现问题时,顺序如下:

block A
block B
block C
block D

换句话说,发生的异常首先由块B处理,然后执行finally子句,只有在异常被重新抛出并在执行堆栈的较低层处理之后(块D)。
请注意,我可能对.NET框架下实际发生的事情有错--我只是介绍了我观察到的结果:)

相关问题