在 C# 中,在運行時間的程式錯誤會透過程序傳播,方法是使用稱為例外狀況的機制。 例外狀況會由發生錯誤的程式碼擲回,然後被可更正該錯誤的程式碼攔截。 例外狀況會由 .NET 執行階段或程式中的程式碼擲回。 當例外狀況被擲出時,它會在呼叫堆疊中向上傳遞,直到找到處理例外狀況的catch
語句為止。 未攔截的例外狀況是由顯示對話框的系統所提供的泛型例外狀況處理程序來處理。
例外狀況是由衍生自 Exception的類別表示。 這個類別會識別例外狀況的類型,並包含具有例外狀況詳細數據的屬性。 擲回例外狀況牽涉到建立例外狀況衍生類別的實例、選擇性地設定例外狀況的屬性,然後使用 關鍵詞擲回物件 throw
。 例如:
class CustomException : Exception
{
public CustomException(string message)
{
}
}
private static void TestThrow()
{
throw new CustomException("Custom exception in TestThrow()");
}
擲回例外狀況之後,執行階段會檢查目前的語句,以確認它是否位於try
區塊內。 如果是,會檢查與 catch
區塊相關聯的任何try
區塊,以查看它們是否可以攔截例外狀況。
Catch
區塊通常會指定例外狀況類型;如果區塊的類型 catch
與例外狀況的類型相同,或例外狀況的基類,則 catch
區塊可以處理 方法。 例如:
try
{
TestThrow();
}
catch (CustomException ex)
{
System.Console.WriteLine(ex.ToString());
}
如果擲回例外狀況的語句不在 try
區塊內,或者封入它的 try
區塊沒有相符的 catch
區塊,執行時會檢查呼叫方法是否有 try
語句和 catch
區塊。 執行時期會繼續在呼叫堆疊中搜尋相容的 catch
區塊。
catch
找到並執行 區塊之後,控件會傳遞至該catch
區塊之後的下一個語句。
try
語句可以包含多個catch
區塊。 會執行可處理例外狀況的第一個 catch
語句;即使這些語句相容,也會忽略下列 catch
任何語句。 將擷取區塊按從最具體(或最衍生)到最不具體的順序排列。 例如:
using System;
using System.IO;
namespace Exceptions
{
public class CatchOrder
{
public static void Main()
{
try
{
using (var sw = new StreamWriter("./test.txt"))
{
sw.WriteLine("Hello");
}
}
// Put the more specific exceptions first.
catch (DirectoryNotFoundException ex)
{
Console.WriteLine(ex);
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex);
}
// Put the least specific exception last.
catch (IOException ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Done");
}
}
}
執行 catch
區塊之前,運行時間會檢查區塊 finally
。
Finally
區塊可讓程式設計人員清除任何可能從中止 try
區塊留下的模糊狀態,或釋放任何外部資源(例如圖形句柄、資料庫連接或檔案流),而不需要等待運行時的垃圾收集器來完成物件的終結。 例如:
static void TestFinally()
{
FileStream? file = null;
//Change the path to something that works on your machine.
FileInfo fileInfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
file?.Close();
}
try
{
file = fileInfo.OpenWrite();
Console.WriteLine("OpenWrite() succeeded");
}
catch (IOException)
{
Console.WriteLine("OpenWrite() failed");
}
}
如果 WriteByte()
擲回例外狀況,而未呼叫 try
,則第二個 file.Close()
區塊中嘗試重新開啟檔案的程式碼會失敗,檔案將保持鎖定狀態。 因為即使擲回例外狀況,finally
區塊也會執行,因此在上一個範例中,finally
區塊可確保檔案正確關閉,並幫助避免錯誤。
如果在擲回例外狀況之後呼叫堆棧上找不到相容的 catch
區塊,則會發生下列三件事之一:
- 如果例外狀況在 完成項內,則會中止完成項,如果有任何,則會呼叫基底完成項。
- 如果呼叫堆疊包含靜態建構函式或靜態字段初始化表達式, TypeInitializationException 則會擲回 ,並將原始例外狀況指派給 InnerException 新例外狀況的屬性。
- 如果到達線程的開頭,線程就會終止。