Pernyataan penanganan pengecualian - throw, , try-catch, try-finallydan try-catch-finally

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 throw ekspresi untuk melemparkan ArgumentException saat array args yang diteruskan kosong:

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • operator penggabungan-null. Contoh berikut menggunakan throw ekspresi untuk melempar ArgumentNullException saat string yang akan ditetapkan ke properti adalah null:

    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 throw ekspresi 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.

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, continuebreak, atau goto), atau
  • propagasi pengecualian di try luar 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 pernyataan .using Pernyataan ini using memastikan bahwa sumber daya yang diperoleh dibuang ketika kontrol meninggalkan using pernyataan. Kompilator mengubah pernyataan menjadi usingtry-finally pernyataan.

Dalam hampir semua kasus finally , blok dijalankan. 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#:

Lihat juga