Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Pernyataan penanganan pengecualian -
Anda menggunakan throw pernyataan dan try untuk bekerja dengan pengecualian.
throw Gunakan pernyataan untuk melemparkan pengecualian.
try Gunakan pernyataan untuk menangkap dan menangani pengecualian yang mungkin terjadi selama eksekusi blok kode.
Pernyataan throw
Pernyataan tersebut throw melemparkan pengecualian:
if (shapeAmount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}
Dalam pernyataan throw e; , hasil ekspresi e harus secara implisit dapat dikonversi ke System.Exception.
Anda dapat menggunakan kelas pengecualian bawaan, misalnya, ArgumentOutOfRangeException atau InvalidOperationException. .NET juga menyediakan metode pembantu berikut untuk melemparkan pengecualian dalam kondisi tertentu: ArgumentNullException.ThrowIfNull dan ArgumentException.ThrowIfNullOrEmpty. Anda juga dapat menentukan kelas pengecualian Anda sendiri yang berasal dari System.Exception. Untuk informasi selengkapnya, lihat Membuat dan melempar pengecualian.
catch Di dalam blok, Anda dapat menggunakan throw; pernyataan untuk melemparkan kembali pengecualian yang ditangani oleh catch blok:
try
{
ProcessShapes(shapeAmount);
}
catch (Exception e)
{
LogError(e, "Shape processing failed.");
throw;
}
Catatan
throw; mempertahankan jejak tumpukan asli pengecualian, yang disimpan di Exception.StackTrace properti . Berlawanan dengan itu, throw e; memperbarui StackTrace properti .e
Ketika pengecualian dilemparkan, runtime bahasa umum (CLR) mencari catch blok yang dapat menangani pengecualian ini. Jika metode yang dijalankan saat ini tidak berisi blok seperti catch itu, CLR melihat metode yang disebut metode saat ini, dan seterusnya di atas tumpukan panggilan. Jika tidak ada catch blok yang ditemukan, CLR mengakhiri utas yang dieksekusi. Untuk informasi selengkapnya, lihat bagian Bagaimana pengecualian ditangani dari spesifikasi bahasa C#.
Ekspresi throw
Anda juga dapat menggunakan throw sebagai ekspresi. Ini mungkin nyaman dalam sejumlah kasus, yang meliputi:
operator kondisional. Contoh berikut menggunakan
throwekspresi untuk melemparkan ArgumentException saat arrayargsyang diteruskan kosong:string first = args.Length >= 1 ? args[0] : throw new ArgumentException("Please supply at least one argument.");operator penggabungan-null. Contoh berikut menggunakan
throwekspresi untuk melempar ArgumentNullException saat string yang akan ditetapkan ke properti adalahnull:public string Name { get => name; set => name = value ?? throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null"); }lambda atau metode berisi ekspresi (expression-bodied). Contoh berikut menggunakan
throwekspresi untuk melemparkan InvalidCastException untuk menunjukkan bahwa konversi ke DateTime nilai tidak didukung:DateTime ToDateTime(IFormatProvider provider) => throw new InvalidCastException("Conversion to a DateTime is not supported.");
Pernyataan try
Anda dapat menggunakan try pernyataan dalam salah satu formulir berikut: try-catch - untuk menangani pengecualian yang mungkin terjadi selama eksekusi kode di dalam try blok, try-finally - untuk menentukan kode yang dijalankan saat kontrol meninggalkan try blok, dan try-catch-finally - sebagai kombinasi dari dua formulir sebelumnya.
Pernyataan try-catch
try-catch Gunakan pernyataan untuk menangani pengecualian yang mungkin terjadi selama eksekusi blok kode. Tempatkan kode di mana pengecualian mungkin terjadi di dalam try blok.
Gunakan klausa tangkapan untuk menentukan jenis dasar pengecualian yang ingin Anda tangani di blok yang catch sesuai:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
Console.WriteLine($"Processing failed: {e.Message}");
}
Anda dapat memberikan beberapa klausa tangkapan:
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.");
}
Ketika pengecualian terjadi, klausul tangkapan diperiksa dalam urutan yang ditentukan, dari atas ke bawah. Maksimum, hanya satu catch blok yang dijalankan untuk pengecualian yang dilemparkan. Seperti yang juga ditunjukkan oleh contoh sebelumnya, Anda dapat menghilangkan deklarasi variabel pengecualian dan hanya menentukan jenis pengecualian dalam klausa tangkapan. Klausa tangkapan tanpa jenis pengecualian yang ditentukan cocok dengan pengecualian apa pun dan, jika ada, harus menjadi klausa tangkapan terakhir.
Jika Anda ingin melemparkan kembali pengecualian yang tertangkap, gunakan throw pernyataan , seperti yang ditunjukkan contoh berikut:
try
{
var result = Process(-3, 4);
Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
LogError(e, "Processing failed.");
throw;
}
Catatan
throw; mempertahankan jejak tumpukan asli pengecualian, yang disimpan di Exception.StackTrace properti . Berlawanan dengan itu, throw e; memperbarui StackTrace properti .e
when Filter pengecualian
Bersama dengan jenis pengecualian, Anda juga dapat menentukan filter pengecualian yang memeriksa pengecualian lebih lanjut dan memutuskan apakah blok yang sesuai catch menangani pengecualian tersebut. Filter pengecualian adalah ekspresi Boolean yang mengikuti when kata kunci, seperti yang ditunjukkan contoh berikut:
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}");
}
Contoh sebelumnya menggunakan filter pengecualian untuk menyediakan satu catch blok untuk menangani pengecualian dari dua jenis yang ditentukan.
Anda dapat memberikan beberapa catch klausul untuk jenis pengecualian yang sama jika dibedakan oleh filter pengecualian. Salah satu klausa tersebut mungkin tidak memiliki filter pengecualian. Jika klausa seperti itu ada, klausul tersebut harus menjadi yang terakhir dari klausul yang menentukan jenis pengecualian tersebut.
catch Jika klausa memiliki filter pengecualian, klausul tersebut dapat menentukan jenis pengecualian yang sama dengan atau kurang diturunkan daripada jenis catch pengecualian klausa yang muncul setelahnya. Misalnya, jika filter pengecualian ada, catch (Exception e) klausa tidak perlu menjadi klausa terakhir.
Filter pengecualian vs. penanganan pengecualian tradisional
Filter pengecualian memberikan keuntungan signifikan daripada pendekatan penanganan pengecualian tradisional. Perbedaan utamanya adalah ketika logika penanganan pengecualian dievaluasi:
-
Filter pengecualian (
when): Ekspresi filter dievaluasi sebelum tumpukan dilepas. Ini berarti tumpukan panggilan asli dan semua variabel lokal tetap utuh selama evaluasi filter. -
Blok tradisional
catch: Blok tangkapan dijalankan setelah tumpukan tidak terlupakan, berpotensi kehilangan informasi penelusuran kesalahan yang berharga.
Berikut adalah perbandingan yang menunjukkan perbedaannya:
public static void DemonstrateStackUnwindingDifference()
{
var localVariable = "Important debugging info";
try
{
ProcessWithExceptionFilter(localVariable);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("filter"))
{
// Exception filter: Stack not unwound yet.
// localVariable is still accessible in debugger.
// Call stack shows original throwing location.
Console.WriteLine($"Caught with filter: {ex.Message}");
Console.WriteLine($"Local variable accessible: {localVariable}");
}
try
{
ProcessWithTraditionalCatch(localVariable);
}
catch (InvalidOperationException ex)
{
// Traditional catch: Stack already unwound.
// Some debugging information may be lost.
if (ex.Message.Contains("traditional"))
{
Console.WriteLine($"Caught with if: {ex.Message}");
Console.WriteLine($"Local variable accessible: {localVariable}");
}
else
{
throw; // Re-throws and further modifies stack trace.
}
}
}
private static void ProcessWithExceptionFilter(string context)
{
throw new InvalidOperationException($"Exception for filter demo: {context}");
}
private static void ProcessWithTraditionalCatch(string context)
{
throw new InvalidOperationException($"Exception for traditional demo: {context}");
}
Keuntungan filter pengecualian
- Pengalaman penelusuran kesalahan yang lebih baik: Karena tumpukan tidak dibatalkan sampai filter cocok, debugger dapat menunjukkan titik kegagalan asli dengan semua variabel lokal utuh.
- Manfaat performa: Jika tidak ada filter yang cocok, pengecualian terus menyebar tanpa overhead pelepasan tumpukan dan pemulihan.
- Kode pembersih: Beberapa filter dapat menangani kondisi yang berbeda dari jenis pengecualian yang sama tanpa memerlukan pernyataan if-else berlapis.
- Pengelogan dan diagnostik: Anda dapat memeriksa dan mencatat detail pengecualian sebelum memutuskan apakah akan menangani pengecualian:
public static void DemonstrateDebuggingAdvantage()
{
var contextData = new Dictionary<string, object>
{
["RequestId"] = Guid.NewGuid(),
["UserId"] = "user123",
["Timestamp"] = DateTime.Now
};
try
{
// Simulate a deep call stack.
Level1Method(contextData);
}
catch (Exception ex) when (LogAndFilter(ex, contextData))
{
// This catch block may never execute if LogAndFilter returns false.
// But LogAndFilter can examine the exception while the stack is intact.
Console.WriteLine("Exception handled after logging");
}
}
private static void Level1Method(Dictionary<string, object> context)
{
Level2Method(context);
}
private static void Level2Method(Dictionary<string, object> context)
{
Level3Method(context);
}
private static void Level3Method(Dictionary<string, object> context)
{
throw new InvalidOperationException("Error in deep call stack");
}
private static bool LogAndFilter(Exception ex, Dictionary<string, object> context)
{
// This method runs before stack unwinding.
// Full call stack and local variables are still available.
Console.WriteLine($"Exception occurred: {ex.Message}");
Console.WriteLine($"Request ID: {context["RequestId"]}");
Console.WriteLine($"Full stack trace preserved: {ex.StackTrace}");
// Return true to handle the exception, false to continue search.
return ex.Message.Contains("deep call stack");
}
Kapan menggunakan filter pengecualian
Gunakan filter pengecualian saat Anda perlu:
- Menangani pengecualian berdasarkan kondisi atau properti tertentu.
- Pertahankan tumpukan panggilan asli untuk penelusuran kesalahan.
- Catat atau periksa pengecualian sebelum memutuskan apakah akan menanganinya.
- Tangani jenis pengecualian yang sama secara berbeda berdasarkan konteks.
public static void HandleFileOperations(string filePath)
{
try
{
// Simulate file operation that might fail.
ProcessFile(filePath);
}
catch (IOException ex) when (ex.Message.Contains("access denied"))
{
Console.WriteLine("File access denied. Check permissions.");
}
catch (IOException ex) when (ex.Message.Contains("not found"))
{
Console.WriteLine("File not found. Verify the path.");
}
catch (IOException ex) when (IsNetworkPath(filePath))
{
Console.WriteLine($"Network file operation failed: {ex.Message}");
}
catch (IOException)
{
Console.WriteLine("Other I/O error occurred.");
}
}
private static void ProcessFile(string filePath)
{
// Simulate different types of file exceptions.
if (filePath.Contains("denied"))
throw new IOException("File access denied");
if (filePath.Contains("missing"))
throw new IOException("File not found");
if (IsNetworkPath(filePath))
throw new IOException("Network timeout occurred");
}
private static bool IsNetworkPath(string path)
{
return path.StartsWith(@"\\") || path.StartsWith("http");
}
Pelestarian pelacakan tumpukan
Filter pengecualian mempertahankan properti asli ex.StackTrace .
catch Jika klausul tidak dapat memproses pengecualian dan melempar ulang, informasi tumpukan asli akan hilang. Filter when tidak melepas tumpukan, jadi jika when filter adalah false, pelacakan tumpukan asli tidak diubah.
Pendekatan filter pengecualian sangat berharga dalam aplikasi di mana mempertahankan informasi penelusuran kesalahan sangat penting untuk mendiagnosis masalah.
Pengecualian dalam metode asinkron dan iterator
Jika pengecualian terjadi dalam fungsi asinkron, pengecualian akan disebarkan ke pemanggil fungsi saat Anda menunggu hasil fungsi, seperti yang ditunjukkan contoh berikut:
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;
}
Jika pengecualian terjadi dalam metode iterator, pengecualian tersebut disebarkan ke pemanggil hanya ketika iterator maju ke elemen berikutnya.
Pernyataan try-finally
Dalam pernyataan try-finally , finally blok dijalankan ketika kontrol meninggalkan try blok. Kontrol mungkin meninggalkan try blok sebagai akibat dari
- eksekusi normal,
- eksekusi pernyataan lompat (yaitu, ,
return,breakcontinue, ataugoto), atau - propagasi pengecualian di
tryluar blok.
Contoh berikut menggunakan finally blok untuk mengatur ulang status objek sebelum kontrol meninggalkan metode :
public async Task HandleRequest(int itemId, CancellationToken ct)
{
Busy = true;
try
{
await ProcessAsync(itemId, ct);
}
finally
{
Busy = false;
}
}
Anda juga dapat menggunakan finally blok untuk membersihkan sumber daya yang dialokasikan yang try digunakan dalam blok.
Catatan
Saat jenis sumber daya mengimplementasikan IDisposable antarmuka atau IAsyncDisposable , pertimbangkan pernyataanusing Pernyataan ini using memastikan bahwa sumber daya yang diperoleh dibuang ketika kontrol meninggalkan using pernyataan. Kompilator mengubah pernyataan menjadi usingtry-finally pernyataan.
Eksekusi finally blok tergantung pada apakah sistem operasi memilih untuk memicu operasi unwind pengecualian. Satu-satunya kasus di mana finally blok tidak dijalankan melibatkan penghentian segera program. Misalnya, penghentian tersebut mungkin terjadi karena Environment.FailFast panggilan atau OverflowExceptionInvalidProgramException pengecualian. Sebagian besar sistem operasi melakukan pembersihan sumber daya yang wajar sebagai bagian dari menghentikan dan membongkar proses.
Pernyataan try-catch-finally
Anda menggunakan try-catch-finally pernyataan untuk menangani pengecualian yang mungkin terjadi selama eksekusi try blok dan menentukan kode yang harus dijalankan saat kontrol meninggalkan try pernyataan:
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;
}
}
Ketika pengecualian ditangani oleh catch blok, finally blok dijalankan setelah eksekusi blok itu catch (bahkan jika pengecualian lain terjadi selama eksekusi catch blok). Untuk informasi tentang catch dan finally blok, lihat Bagian try-catch pernyataan dan try-finally Pernyataan.
Spesifikasi bahasa C#
Untuk informasi selengkapnya, lihat bagian berikut dari spesifikasi bahasa C#: