例外狀況處理陳述式 - throw
、try-catch
、try-finally
和 try-catch-finally
您可以使用 throw
和 try
陳述式來處理例外狀況。 使用 throw
陳述式 來擲回例外狀況。 使用 try
陳述式 來攔截並處理程式碼區塊執行期間可能發生的例外狀況。
throw
陳述式
throw
陳述式會擲回例外狀況:
if (shapeAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}
在 throw e;
陳述式中,運算式 e
的結果必須隱含轉換成 System.Exception。
您可以使用內建例外狀況類別,例如 ArgumentOutOfRangeException 或 InvalidOperationException。 .NET 也提供下列協助程式方法,以在特定情況下擲回例外狀況:ArgumentNullException.ThrowIfNull 和 ArgumentException.ThrowIfNullOrEmpty。 您也可以定義衍生自 System.Exception 的自有例外狀況類別。 如需詳細資訊,請參閱建立和擲回例外狀況。
在 catch
區塊內,您可以使用 throw;
陳述式來重新擲回 catch
區塊所處理的例外狀況:
try
{
ProcessShapes(shapeAmount);
}
catch (Exception e)
{
LogError(e, "Shape processing failed.");
throw;
}
注意
throw;
會保留例外狀況的原始堆疊追蹤,其儲存在 Exception.StackTrace 屬性中。 相反地,throw e;
會更新 e
的 StackTrace 屬性。
擲回例外狀況時,Common Language Runtime (CLR) 會尋找可處理此例外狀況的 catch
區塊。 如果目前執行的方法不包含這類 catch
區塊,CLR 會在呼叫堆疊上查看呼叫目前方法的方法,依此類推。 如果找不到 catch
區塊,CLR 就會終止執行中的執行緒。 如需詳細資訊,請參閱 C# 語言規格的如何處理例外狀況一節。
throw
運算式
您也可以使用 throw
作為運算式。 這在一些案例中可能很方便,包括:
條件運算子。 下列範例會使用
throw
運算式,在傳遞的陣列args
空白時擲回 ArgumentException:string first = args.Length >= 1 ? args[0] : throw new ArgumentException("Please supply at least one argument.");
Null 聯合運算子。 下列範例會使用
throw
運算式,在要指派給屬性的字串為null
時擲回 ArgumentNullException:public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null"); }
運算式主體 ambda 或方法。 下列範例會使用
throw
運算式來擲回 InvalidCastException,指出不支援轉換成 DateTime 值:DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Conversion to a DateTime is not supported.");
try
陳述式
您可以使用下列任何形式的 try
陳述式:try-catch
- 處理在 try
區塊內執行程式碼時可能發生的例外狀況,try-finally
- 指定當控制項離開 try
區塊時執行的程式碼,以及 try-catch-finally
- 做為上述兩種形式的組合。
try-catch
陳述式
使用 try-catch
陳述式 來處理程式碼區塊執行期間可能發生的例外狀況。 將可能發生例外狀況的程式碼放在 try
區塊內。 使用 catch 子句來指定您想要在對應 catch
區塊中處理的例外狀況基底類型:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
您可以提供數個 catch 子句:
try
{
var result = await ProcessAsync(-3, 4, cancellationToken);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
catch (OperationCanceledException)
{
Console.WriteLine("Processing is cancelled.");
}
發生例外狀況時,會以指定的順序 (從上到下) 檢查 catch 子句。 最多只會針對任何擲回的例外狀況執行一個 catch
區塊。 如上述範例所示,您可以省略例外狀況變數的宣告,只在 catch 子句中指定例外狀況類型。 不含任何指定例外狀況類型的 catch 子句符合任何例外狀況,如果存在,則必須是最後一個 catch 子句。
如果您想要重新擲回攔截到的例外狀況,請使用 throw
陳述式,如下列範例所示:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
LogError(e, "Processing failed.");
throw;
}
注意
throw;
會保留例外狀況的原始堆疊追蹤,其儲存在 Exception.StackTrace 屬性中。 相反地,throw e;
會更新 e
的 StackTrace 屬性。
when
例外狀況篩選條件
除了例外狀況類型,您也可以指定例外狀況篩選條件,進一步檢查例外狀況,並決定對應的 catch
區塊是否處理該例外狀況。 例外狀況篩選條件是接在 when
關鍵字之後的布林運算式,如下列範例所示:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e) when (e is ArgumentException || e is DivideByZeroException)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
上述範例會使用例外狀況篩選來提供單一 catch
區塊,以處理兩個指定類型的例外狀況。
您可以為相同的例外狀況類型提供數個 catch
子句,前提是這些子句是由例外狀況篩選條件區別。 其中一個子句可能沒有任何例外狀況篩選條件。 如果這類子句存在,其必須是指定該例外狀況類型的最後一個子句。
如果 catch
子句具有例外狀況篩選條件,其指定的例外狀況類型可與出現在其後之 catch
子句的例外狀況類型相同或衍生程度較低。 例如,如果例外狀況篩選條件存在,則 catch (Exception e)
子句不需要是最後一個子句。
非同步和迭代器方法的例外狀況
如果在非同步函式中發生例外狀況,當您 await 函式的結果時,它會傳播至函式的呼叫端,如下列範例所示:
public static async Task Run()
{
try
{
Task<int> processing = ProcessAsync(-1);
Console.WriteLine("Launched processing.");
int result = await processing;
Console.WriteLine($"Result: {result}.");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
// Output:
// Launched processing.
// Processing failed: Input must be non-negative. (Parameter 'input')
}
private static async Task<int> ProcessAsync(int input)
{
if (input < 0)
{
throw new ArgumentOutOfRangeException(nameof(input), "Input must be non-negative.");
}
await Task.Delay(500);
return input;
}
如果迭代器方法發生例外狀況,則只有在迭代器進到下一個元素時,才會傳播至呼叫端。
try-finally
陳述式
在 try-finally
陳述式中,當控制項離開 try
區塊時,就會執行 finally
區塊。 控制項可能會由於下列事項而離開 try
區塊:
- 正常執行,
- 執行 跳躍陳述式 (也就是
return
、break
、continue
或goto
),或 - 傳播
try
區塊以外的例外狀況。
下列範例會使用 finally
區塊,在控制項離開方法之前重設物件的狀態:
public async Task HandleRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
finally
{
Busy = false;
}
}
您也可以使用 finally
區塊來清除 try
區塊中使用的配置資源。
注意
當資源的類型實作 IDisposable 或 IAsyncDisposable 介面時,請考慮 using
陳述式。 using
陳述式可確保當控制項離開 using
陳述式時,會處置已取得的資源。 編譯器會將 using
陳述式轉換成 try-finally
陳述式。
執行 finally
區塊取決於作業系統是否選擇觸發例外狀況回溯作業。 未執行 finally
區塊的唯一情況涉及立即終止程式。 例如,這類終止可能會因為 Environment.FailFast 呼叫或 OverflowException 或 InvalidProgramException 例外狀況而發生。 大部分的作業系統會在停止和卸載流程中執行合理的資源清除。
try-catch-finally
陳述式
您可以使用 try-catch-finally
陳述式來處理 try
區塊執行期間可能發生的例外狀況,並指定當控制項離開 try
陳述式時必須執行的程式碼:
public async Task ProcessRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
catch (Exception e) when (e is not OperationCanceledException)
{
LogError(e, $"Failed to process request for item ID {itemId}.");
throw;
}
finally
{
Busy = false;
}
}
當 catch
區塊處理例外狀況時,finally
區塊會在執行該 catch
區塊之後執行 (即使執行 catch
區塊期間發生另一個例外狀況也一樣)。 如需 catch
和 finally
區塊的相關資訊,請分別參閱try-catch
陳述式和 try-finally
陳述式章節。
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的下列幾節: