[英]Order of execution of try catch and finally block
我對 try、catch 和 finally 塊執行的順序感到困惑。
我也想知道什么時候應該使用 try-catch 塊,我應該在 try-catch 塊中放什么? 我還想知道 try 塊中是否出現異常,然后是否采取了對應於 try 塊的操作,那么首先執行哪個 catch 或最后執行(始終要執行)? 執行完這兩個之后,控制是返回到 try 塊還是離開它?
如果您有(注意:這不是有效的 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
塊中執行此操作 - 該塊總是被執行,從而保證文件的關閉。
在這里查看更多信息:
try ... catch
塊用於捕獲異常。 在try
塊中,您放置您期望可能引發異常的代碼。
如果沒有發生異常,則try
塊中的代碼按預期完成。 如果有finally
塊,那么接下來會執行。
如果確實發生了異常,則執行跳轉到第一個匹配的catch
塊的開頭。 一旦該代碼完成,finally 塊(如果存在)就會被執行。 執行不會返回到try
塊。
你幾乎不應該使用 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
,我們將不知道它意味着什么,或者如何處理它。 不應捕獲您實際上不知道如何糾正的任何異常,除非可能將信息添加到異常,記錄並重新拋出。
這是一個例子:
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
}
我想詳細說明一下,並擴展 @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 框架下實際發生的事情有誤——我只是展示了我觀察到的結果:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.