try-catch (C# 參考)try-catch (C# Reference)

try-catch 陳述式包含 try 區塊後面接著一個或多個 catch 子句,指定不同例外狀況的處理常式。The try-catch statement consists of a try block followed by one or more catch clauses, which specify handlers for different exceptions.

擲回例外狀況時,Common Language Runtime (CLR) 會尋找處理此例外狀況的 catch 陳述式。When an exception is thrown, the common language runtime (CLR) looks for the catch statement that handles this exception. 如果目前執行的方法不包含這類 catch 區塊,CLR 會在呼叫堆疊上查看呼叫目前方法的方法,依此類推。If the currently executing method does not contain such a catch block, the CLR looks at the method that called the current method, and so on up the call stack. 如果找不到 catch 區塊,則 CLR 會向使用者顯示未處理的例外狀況訊息,並停止執行程式。If no catch block is found, then the CLR displays an unhandled exception message to the user and stops execution of the program.

try 區塊包含可能會造成例外狀況的防護程式碼。The try block contains the guarded code that may cause the exception. 區塊會執行直到例外狀況擲回,或已成功完成。The block is executed until an exception is thrown or it is completed successfully. 例如,以下的轉換 null 物件嘗試會引發 NullReferenceException 例外狀況:For example, the following attempt to cast a null object raises the NullReferenceException exception:

object o2 = null;
try
{
    int i2 = (int)o2;   // Error
}

雖然可以不含引數使用 catch 子句,來攔截任何類型的例外狀況,不建議使用這種用法。Although the catch clause can be used without arguments to catch any type of exception, this usage is not recommended. 一般而言,您應該只攔截那些您知道如何從中復原的例外狀況。In general, you should only catch those exceptions that you know how to recover from. 因此,您應該永遠指定衍生自 System.Exception 的物件引數,例如:Therefore, you should always specify an object argument derived from System.Exception For example:

catch (InvalidCastException e)
{
}

您可以在相同 try catch 陳述式中的子句中使用多個特定的 catchIt is possible to use more than one specific catch clause in the same try-catch statement. 在此情況下,catch 子句的順序很重要,因為會依順序檢查 catch 子句。In this case, the order of the catch clauses is important because the catch clauses are examined in order. 在較不特定的例外狀況之前攔截較特定的例外狀況。Catch the more specific exceptions before the less specific ones. 如果您排序 catch 區塊,使得永遠不會達到較新的區塊,編譯器會產生錯誤。The compiler produces an error if you order your catch blocks so that a later block can never be reached.

使用 catch 引數是篩選您想要處理的例外狀況的一種方式。Using catch arguments is one way to filter for the exceptions you want to handle. 您也可以使用例外狀況篩選,進一步檢查例外狀況來決定是否要處理。You can also use an exception filter that further examines the exception to decide whether to handle it. 如果例外狀況篩選會傳回 false,則搜尋處理常式會繼續。If the exception filter returns false, then the search for a handler continues.

catch (ArgumentException e) when (e.ParamName == "…")
{
}

例外狀況篩選條件優於攔截和重新擲回 (說明如下所示),因為篩選條件不會損壞堆疊。Exception filters are preferable to catching and rethrowing (explained below) because filters leave the stack unharmed. 如果之後的處理常式傾印堆疊,您可以看到例外狀況原本來自何處,而不是只重新擲回的最後一個位置。If a later handler dumps the stack, you can see where the exception originally came from, rather than just the last place it was rethrown. 例外狀況篩選條件運算式的常見用法是記錄。A common use of exception filter expressions is logging. 您可以建立一律會傳回 false 同時會輸出到記錄檔的篩選,您可以持續記錄例外狀況,而不必處理它們並重新擲回。You can create a filter that always returns false that also outputs to a log, you can log exceptions as they go by without having to handle them and rethrow.

throw 陳述式可以在 catch 區塊中用來重新擲回 catch 陳述式攔截到的例外狀況。A throw statement can be used in a catch block to re-throw the exception that is caught by the catch statement. 下列範例會從 IOException 例外狀況擷取來源資訊,然後將例外狀況擲回父方法。The following example extracts source information from an IOException exception, and then throws the exception to the parent method.

catch (FileNotFoundException e)
{
    // FileNotFoundExceptions are handled here.
}
catch (IOException e)
{
    // Extract some information from this exception, and then 
    // throw it to the parent method.
    if (e.Source != null)
        Console.WriteLine("IOException source: {0}", e.Source);
    throw;
}

您可以攔截一個例外狀況,並擲回不同的例外狀況。You can catch one exception and throw a different exception. 執行此動作時,請將您攔截的例外狀況指定為內部例外狀況,如下列範例所示。When you do this, specify the exception that you caught as the inner exception, as shown in the following example.

catch (InvalidCastException e) 
{
    // Perform some action here, and then throw a new exception.
    throw new YourCustomException("Put your error message here.", e);
}

指定的條件為 true 時,您可以也重新擲回例外狀況時,如下列範例所示。You can also re-throw an exception when a specified condition is true, as shown in the following example.

catch (InvalidCastException e)
{
    if (e.Data == null)
    {
        throw;
    }
    else
    {
        // Take some action.
    }
}

注意

也可以使用例外狀況篩選,以通常更為簡潔的方式取得類似的結果 (以及未修改堆疊,如本文件稍早所述)。It is also possible to use an exception filter to get a similar result in an often cleaner fashion (as well as not modifying the stack, as explained earlier in this document). 下列範例的呼叫端行為類似於上一個範例。The following example has a similar behavior for callers as the previous example. 此函式會在 e.Datanull 時,將 InvalidCastException 擲回呼叫端。The function throws the InvalidCastException back to the caller when e.Data is null.

catch (InvalidCastException e) when (e.Data != null) 
{
    // Take some action.
}

try 區塊內,僅初始化其中宣告的變數。From inside a try block, initialize only variables that are declared therein. 否則,在區塊的執行完成之前,可能會發生例外狀況。Otherwise, an exception can occur before the execution of the block is completed. 例如,在下列程式碼範例中,變數 n 是在 try 區塊內初始化。For example, in the following code example, the variable n is initialized inside the try block. Write(n) 陳述式中的 try 區塊外嘗試使用此變數,將產生編譯器錯誤。An attempt to use this variable outside the try block in the Write(n) statement will generate a compiler error.

static void Main() 
{
    int n;
    try 
    {
        // Do not initialize this variable here.
        n = 123;
    }
    catch
    {
    }
    // Error: Use of unassigned local variable 'n'.
    Console.Write(n);
}

如需 catch 的詳細資訊,請參閱 try-catch-finallyFor more information about catch, see try-catch-finally.

非同步方法中的例外狀況Exceptions in async methods

非同步方法會標記 async 修飾詞,而且通常包含一或多個 await 運算式或陳述式。An async method is marked by an async modifier and usually contains one or more await expressions or statements. await 運算式會將 await 運算子套用至 TaskTask<TResult>An await expression applies the await operator to a Task or Task<TResult>.

當控制項到達 await 方法時,方法中的進度會暫停,直到等候的工作完成。When control reaches an await in the async method, progress in the method is suspended until the awaited task completes. 當工作完成時,方法中的執行可以繼續。When the task is complete, execution can resume in the method. 如需詳細資訊,請參閱使用 async 和 await 進行非同步程式設計非同步程式中的控制流程For more information, see Asynchronous Programming with async and await and Control Flow in Async Programs.

套用 await 完成的工作可能因為傳回工作的方法中未處理的例外狀況而處於錯誤的狀態。The completed task to which await is applied might be in a faulted state because of an unhandled exception in the method that returns the task. 等候工作擲回例外狀況。Awaiting the task throws an exception. 如果傳回工作的非同步程序被取消,工作也可能以取消的狀態結束。A task can also end up in a canceled state if the asynchronous process that returns it is canceled. 等候已取消的工作會擲回 OperationCanceledExceptionAwaiting a canceled task throws an OperationCanceledException. 如需如何取消非同步處理序的詳細資訊,請參閱微調非同步應用程式For more information about how to cancel an asynchronous process, see Fine-Tuning Your Async Application.

若要攔截例外狀況,請在 try 區塊中等候工作,並在關聯的 catch 區塊中攔截例外狀況。To catch the exception, await the task in a try block, and catch the exception in the associated catch block. 如需範例,請參閱非同步方法範例一節。For an example, see the Async method example section.

工作可能處於錯誤狀態,因為在等候的非同步方法中發生多個例外狀況。A task can be in a faulted state because multiple exceptions occurred in the awaited async method. 例如,工作可能是對 Task.WhenAll 呼叫的結果。For example, the task might be the result of a call to Task.WhenAll. 當您等候這類工作時,只會攔截到其中一個例外狀況,而且您無法預測會攔截的例外狀況。When you await such a task, only one of the exceptions is caught, and you can't predict which exception will be caught. 如需範例,請參閱 Task.WhenAll 範例一節。For an example, see the Task.WhenAll example section.

範例Example

在下列範例中,try 區塊包含可能會造成例外狀況的對 ProcessString 方法的呼叫。In the following example, the try block contains a call to the ProcessString method that may cause an exception. catch 子句包含只會在螢幕上顯示訊息的例外狀況處理常式。The catch clause contains the exception handler that just displays a message on the screen. MyMethod 內呼叫 throw 陳述式時 ,系統會尋找 catch 陳述式,並顯示訊息 Exception caughtWhen the throw statement is called from inside MyMethod, the system looks for the catch statement and displays the message Exception caught.

class TryFinallyTest
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        string s = null; // For demonstration purposes.

        try
        {               
            ProcessString(s);
        }
        catch (Exception e)
        {
            Console.WriteLine("{0} Exception caught.", e);
        }
    }
}
/*
Output:
System.ArgumentNullException: Value cannot be null.
   at TryFinallyTest.Main() Exception caught.
 * */

兩個 catch 區塊範例Two catch blocks example

在下列範例中,使用了兩個 catch 區塊,並會攔截會先出現的最特定例外狀況。In the following example, two catch blocks are used, and the most specific exception, which comes first, is caught.

若要攔截最不特定的例外狀況,您可以使用下列陳述式取代 ProcessString 中的 throw 陳述式:throw new Exception()To catch the least specific exception, you can replace the throw statement in ProcessString with the following statement: throw new Exception().

如果您先在範例中放置最特定的 catch 區塊,會出現下列錯誤訊息:A previous catch clause already catches all exceptions of this or a super type ('System.Exception')If you place the least-specific catch block first in the example, the following error message appears: A previous catch clause already catches all exceptions of this or a super type ('System.Exception').

class ThrowTest3
{
    static void ProcessString(string s)
    {
        if (s == null)
        {
            throw new ArgumentNullException();
        }
    }

    static void Main()
    {
        try
        {
            string s = null;
            ProcessString(s);
        }
        // Most specific:
        catch (ArgumentNullException e)
        {
            Console.WriteLine("{0} First exception caught.", e);
        }
        // Least specific:
        catch (Exception e)
        {
            Console.WriteLine("{0} Second exception caught.", e);
        }
    }
}
/*
 Output:
 System.ArgumentNullException: Value cannot be null.
 at Test.ThrowTest3.ProcessString(String s) ... First exception caught.
*/

非同步方法範例Async method example

下列範例說明非同步方法的例外狀況處理。The following example illustrates exception handling for async methods. 若要擷取非同步工作擲回的例外狀況,請將 await 運算式放置在 try 區塊中,並攔截 catch 區塊中的例外狀況。To catch an exception that an async task throws, place the await expression in a try block, and catch the exception in a catch block.

取消註解範例中的 throw new Exception 行來示範例外狀況處理。Uncomment the throw new Exception line in the example to demonstrate exception handling. 工作的 IsFaulted 屬性設定為 True,工作的 Exception.InnerException 屬性設定為例外狀況,並在 catch 區塊攔截例外狀況。The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block.

取消註解 throw new OperationCanceledException 行來示範取消非同步處理序時會發生的情況。Uncomment the throw new OperationCanceledException line to demonstrate what happens when you cancel an asynchronous process. 工作的 IsCanceled 屬性設定為 true,並在 catch 區塊攔截例外狀況。The task's IsCanceled property is set to true, and the exception is caught in the catch block. 在不適用這個範例的部分情況下,工作的 IsFaulted 屬性會設定為 trueIsCanceled 設為 falseUnder some conditions that don't apply to this example, the task's IsFaulted property is set to true and IsCanceled is set to false.

public async Task DoSomethingAsync()
{
    Task<string> theTask = DelayAsync();

    try
    {
        string result = await theTask;
        Debug.WriteLine("Result: " + result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception Message: " + ex.Message);
    }
    Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
    Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
    if (theTask.Exception != null)
    {
        Debug.WriteLine("Task Exception Message: "
            + theTask.Exception.Message);
        Debug.WriteLine("Task Inner Exception Message: "
            + theTask.Exception.InnerException.Message);
    }
}

private async Task<string> DelayAsync()
{
    await Task.Delay(100);

    // Uncomment each of the following lines to
    // demonstrate exception handling.

    //throw new OperationCanceledException("canceled");
    //throw new Exception("Something happened.");
    return "Done";
}

// Output when no exception is thrown in the awaited method:
//   Result: Done
//   Task IsCanceled: False
//   Task IsFaulted:  False

// Output when an Exception is thrown in the awaited method:
//   Exception Message: Something happened.
//   Task IsCanceled: False
//   Task IsFaulted:  True
//   Task Exception Message: One or more errors occurred.
//   Task Inner Exception Message: Something happened.

// Output when a OperationCanceledException or TaskCanceledException
// is thrown in the awaited method:
//   Exception Message: canceled
//   Task IsCanceled: True
//   Task IsFaulted:  False

Task.WhenAll 範例Task.WhenAll example

下列範例說明多項工作可能會導致多個例外狀況的例外狀況處理。The following example illustrates exception handling where multiple tasks can result in multiple exceptions. try 區塊會等候對 Task.WhenAll 的呼叫傳回的工作。The try block awaits the task that's returned by a call to Task.WhenAll. 當套用所有項目的三項工作都完成時,工作即完成。The task is complete when the three tasks to which WhenAll is applied are complete.

這三個工作都會造成例外狀況。Each of the three tasks causes an exception. catch 區塊會逐一查看例外狀況,這可以在 Task.WhenAll 傳回的工作的 Exception.InnerExceptions 屬性中找到。The catch block iterates through the exceptions, which are found in the Exception.InnerExceptions property of the task that was returned by Task.WhenAll.

public async Task DoMultipleAsync()
{
    Task theTask1 = ExcAsync(info: "First Task");
    Task theTask2 = ExcAsync(info: "Second Task");
    Task theTask3 = ExcAsync(info: "Third Task");

    Task allTasks = Task.WhenAll(theTask1, theTask2, theTask3);

    try
    {
        await allTasks;
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception: " + ex.Message);
        Debug.WriteLine("Task IsFaulted: " + allTasks.IsFaulted);
        foreach (var inEx in allTasks.Exception.InnerExceptions)
        {
            Debug.WriteLine("Task Inner Exception: " + inEx.Message);
        }
    }
}

private async Task ExcAsync(string info)
{
    await Task.Delay(100);
    
    throw new Exception("Error-" + info);
}

// Output:
//   Exception: Error-First Task
//   Task IsFaulted: True
//   Task Inner Exception: Error-First Task
//   Task Inner Exception: Error-Second Task
//   Task Inner Exception: Error-Third Task

C# 語言規格C# language specification

如需詳細資訊,請參閱 C# 語言規格try 陳述式一節。For more information, see The try statement section of the C# language specification.

另請參閱See also