Saturday, December 24, 2016

Bab 12. Pemrograman C# Belajar Dari Contoh



12. Penanganan Eksepsi







Eksepsi adalah sebuah error yang terjadi pada saat program dijalankan. Keuntungan dari penanganan eksepsi adalah bahwa penanganan error secara otomatis dilakukan oleh kode. Penanganan eksepsi penting karena C# mendefinisikan eksepsi standar untuk error program yang umum dijumpai, seperti pembagian-oleh-nol atau indeks-di-luar-rentang.


Menggunakan try dan catch
Inti dari penanganan eksepsi adalah try dan catch. Kedua katakunci ini bekerja sama, dan Anda tidak bisa memiliki catch tanpa memiliki try. Berikut adalah bentuk umum dari blok penanganan eksepsi try/catch:

try {
  // blok kode untuk memantau error
}

catch (TipeEksepsi1 eksOb) {
  // handler untuk TipeEksepsi1
}

catch (TipeEksepsi2 eksOb) {
  // handler untuk TipeEksepsi2
}
.
.
.

Di sini, TipeEksepsi merupakan tipe eksekusi yang terjadi. Ketika sebuah eksepsi dilemparkan, ia ditangkap oleh klausa catch terkait, yang kemudian memproses eksepsi tersebut. Seperti yang ditunjukkan oleh bentuk umum, lebih dari satu klausa catch dapat diasosiasikan dengan sebuah try. Tipe eksepsi menentukan catch mana yang akan dieksekusi. Yaitu, jika tipe eksepsi yang dispesifikasi oleh sebuah catch cocok dengan eksepsi, maka catch tersebut akan dieksekusi (dan semua catch lainnya akan diabaikan atau dilompati). Ketika sebuah eksepsi ditangkap, variabel eksepsi eksOb akan menerima nilainya.

Sebenarnya, penspesifikasian eksOb bersifat opsional. Jika handler eksepsi tidak memerlukan akses terhadap objek eksepsi (yang merupakan kasus yang umum dijumpai), maka tidak perlu eksOb dispesifikasi. Tipe eksepsi sendiri sudah cukup. Karena alasa ini, banyak contoh pada bab ini tidak akan menspesifikasi eksOb.


Berikut adalah satu hal penting yang perlu diingat: Jika tidak ada eksepsi yang dilemparkan, maka blok try akan berakhir dengan normal, dan semua klausa catch akan dilompati. Eksekusi akan dilakukan selanjutnya terhadap statemen pertama setelah catch terakhir. Jadi, blok catch akan dieksekusi hanya dan hanya jika eksepsi dilemparkan.


Contoh Sederhana Eksepsi
Berikut adalah sebuah contoh sederhana yang mengilustrasikan bagaimana memonitor dan menangkap sebuah eksepsi. Seperti Anda ketahui, adalah sebuah error untuk mengindeks sebuah array di luar batasnya. Ketika error ini terjadi, CLR akan melempar sebuah IndexOutOfRange-Exception, yang merupakan eksepsi standar yang didefinisikan oleh .NET Framework. Program berikut secara sengaja membangkitkan eksepsi semacam itu dan kemudian menangkapnya:

// Demonstrasi penanganan eksepsi.
using System;

class DemoEksepsi1 {
    static void Main() {
        int[] angka = new int[4];
       
        try
        {
            Console.WriteLine("Sebelum eksepsi dibangkitkan.");
           
            // Membangkitkan eksepsi di-luar-batas.
            for (int i = 0; i < 10; i++)
            {
                angka[i] = i;
                Console.WriteLine("angka[{0}]: {1}", i, angka[i]);
            }
           
            Console.WriteLine("ini tidak akan ditampilkan");
        }
       
        catch (IndexOutOfRangeException)
        {
            // Menangkap eksepsi.
            Console.WriteLine("Indeks di luar batas array!");
        }
        Console.WriteLine("Setelah blok catch.");
    }
}

Keluaran program ditampilkan di sini:

Sebelum eksepsi dibangkitkan.
angka[0]: 0
angka[1]: 1
angka[2]: 2
angka[3]: 3
Indeks di luar batas array!
Setelah blok catch.


Perhatikan bahwa angka merupakan sebuah array int beranggotakan empat elemen. Namun, loop for mencoba untuk mengindeks angka dari 0 sampai 9, yang menyebabkan sebuah IndexOutOfRangeException ketika nilai indeks i bernilai 4.

Meskipun cukup pendek, program sebelumnya mengilustrasikan beberapa poin kunci tentang penanganan eksepsi. Pertama, kode yang Anda inginkan untuk memonitor error adalah kode yang dimuat di dalam blok try. Kedua, ketika sebuah eksepsi terjadi (pada kasus ini, karena mencoba untuk mengindeks angka di luar batasnya di dalam loop for), eksepsi akan dilemparkan ke luar blok try dan ditangkap oleh catch. Pada titik ini, kendali program beralih ke blok catch, dan blok try berhenti. Ingat, catch tidak dipanggil, tetapi kendali program yang berpindah ke dalam blok catch. Jadi, statemen WriteLine() yang berada setelah indeks di-luar-batas tidak akan pernah dieksekusi. Setelah blok catch dieksekusi, kendali program berlanjut ke statemen setelah catch. Oleh karena itu, adalah tugas handler eksepsi Anda untuk mengoreksi masalah yang menyebabkan eksepsi sehingga eksekusi program dapat berlanjut dengan normal.

Perhatikan bahwa tidak ada variabel eksepsi yang dispesifikasi di dalam klausa catch. Tetapi, hanya tipe eksepsi (IndexOutOfRangeException pada kasus ini) yang diperlukan. Seperti yang telah dijelaskan sebelumnya, variabel eksepsi hanya diperlukan ketika diinginkan untuk mengakses objek eksepsi. Pada beberapa kasus, nilai dari objek eksepsi dapat dipakai oleh handler eksepsi untuk mendapatkan informasi tentang error, tetapi pada banyak kasus, hal itu tidak diperlukan.

Seperti yang telah dijelaskan, jika tidak ada eksepsi yang dilemparkan oleh blok try, maka klausa catch tidak akan dieksekusi dan kendali program berpindah ke statemen setelah catch. Untuk menegaskannya, Anda bisa mengubah loop for dari

for(int i=0; i < 10; i++) {

menjadi

for(int i=0; i < angka.Length; i++) {

Sekarang, loop tidak mencoba untuk mengakses di luar batas angka. Jadi, tidak ada eksepsi yang dibangkitkan, dan blok catch tidak akan dieksekusi.


Contoh Eksepsi Kedua
Adalah penting untuk memahami bahwa semua kode yang dieksekusi di dalam suatu blok try dimonitor (dalam kaitannya dengan eksepsi). Hal ini juga mencakup eksepsi yang bisa saja dibangkitkan oleh sebuah metode yang dipanggil dari dalam blok try. Eksepsi yang dilemparkan oleh sebuah metode dari dalam blok try dapat ditangkap oleh klausa catch yang berkaitan dengan blok try tersebut, bila diasumsikan  bahwa metode tersebut tidak menangkap eksepsi tersebut.

Sebagai contoh, perhatikan program berikut: Main() mencantumkan sebuah blok try, yang dari dalam blok tersebut metode BangkitEksepsi() dipanggil. Di dalam BangkitEksepsi(), sebuah IndexOutOfRangeException dibangkitkan. Eksepsi ini tidak ditangkap oleh BangkitEksepsi(). Namun, karena BangkitEksepsi() dipanggil dari dalam sebuah blok try di dalam Main(), eksepsi tersebut ditangkap oleh statemen catch yang berkaitan dengan try tersebut.
/* Sebuah eksepsi dapat dibangkitkan dari suatu metode
   dan ditangkap oleh metode lain. */
using System;

class TestEksp {

    // Membangkitkan sebuah eksepsi.
    public static void BangkitEksepsi() {
        int[] angka = new int[4];
        Console.WriteLine("Sebelum eksepsi dibangkitkan.");

        // Menghasilkan eksepsi indeks di luar batas.
        for(int i=0; i < 10; i++) {
            angka[i] = i;
            Console.WriteLine("angka[{0}]: {1}", i, angka[i]);
        }

        Console.WriteLine("ini tidak akan ditampilkan");
    }
}

class DemoEksp2
{
    static void Main()
    {
        try
        {
            TestEksp.BangkitEksepsi();
        }
       
        catch (IndexOutOfRangeException)
        {
            // Menangkap eksepsi.
            Console.WriteLine("Indeks di luar batas!");
        }
       
        Console.WriteLine("Setelah blok catch.");
    }
}

Program menghasilkan keluaran berikut:

Sebelum eksepsi dibangkitkan.
angka[0]: 0
angka[1]: 1
angka[2]: 2
angka[3]: 3
Indeks di luar batas!
Setelah blok catch.

Seperti yang telah dijelaskan, karena BangkitEksepsi() dipanggil dari dalam sebuah blok try, eksepsi yang dibangkitkannya ditangkap oleh catch di dalam Main(). Anda perlu memahami bahwa jika BangkitEksepsi() menangkap eksepsi tersebut, maka kendali program tidak akan kembali lagi kepada Main().



Konsekuensi Dari Ekseksepsi Yang Tidak Ditangkap
Penangkapan eksepsi standar, seperti yang didemonstrasikan pada program sebelumnya, mempunyai tujuan: Mencegah terminasi program secara abnormal. Ketika sebuah eksepsi dilemparkan, eksepsi tersebut harus ditangkap oleh sepotong kode di suatu tempat. Secara umum, jika program Anda tidak menangkap eksepsi, maka eksepsi tersebut akan ditangkap oleh sistem. Masalah yang terjadi adalah bahwa sistem akan melaporkan error dan menghentikan program. Sebagai contoh, pada contoh ini, eksepsi indeks-di-luar-batas tidak ditangkap oleh program:

// Membiarkan sistem C# menangani error.
using System;

class TidakTertangani {
  static void Main() {
    int[] angka = new int[4];

    Console.WriteLine("Sebelum eksepsi dibangkitkan.");
   
    // Membangkitkan eksepsi indeks-di-luar-batas.
    for (int i = 0; i < 10; i++)
    {
      angka[i] = i;
      Console.WriteLine("angka[{0}]: {1}", i, angka[i]);
    }
  }
}

Ketika error indeks array terjadi, eksekusi dihentikan dan ditampilkan keluaran seperti ini:

Sebelum eksepsi dibangkitkan.
angka[0]: 0
angka[1]: 1
angka[2]: 2
angka[3]: 3

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array. at TidakTertangani.Main() in E:\...

Seperti disebutkan sebelumnya, tipe eksepsi harus cocok dengan tipe yang dispesifikasi di dalam catch. Jika tidak, maka eksepsi tidak akan ditangkap. Sebagai contoh, program berikut mencoba untuk menangkap error batas array dengan sebuah catch dengan tipe eksepsi DivideByZero-Exception. Ketika pengaksesan array di luar batas, maka eksepsi IndexOutOfRangeException dibangkitkan, tetapi tidak ditangkap oleh catch tersebut. Hal ini menyebabkan program berhenti secara abnormal.

// Ini tidak akan bekerja!.
using System;

class EksepsiSalahTipe
{
    static void Main()
    {
        int[] angka = new int[4];

        try
        {
            Console.WriteLine("Sebelum eksepsi dibangkitkan.");

            // Membangkitkan eksepsi di-luar-batas.
            for (int i = 0; i < 10; i++)
            {
                angka[i] = i;
                Console.WriteLine("angka[{0}]: {1}", i, angka[i]);
            }

            Console.WriteLine("ini tidak akan ditampilkan");
        }

         /* Tidak dapat menangkap error batas array dengan tipe
            eksepsi DivideByZeroException. */
        catch (DivideByZeroException)
        {
            // Menangkap eksepsi.
            Console.WriteLine("Indeks di luar batas array!");
        }

        Console.WriteLine("Setelah blok catch.");
    }
}

Keluaran program ditampilkan di sini:

Sebelum eksepsi dibangkitkan.
angka[0]: 0
angka[1]: 1
angka[2]: 2
angka[3]: 3

Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array. at EksepsiSalahTipe.Main() in ...


Contoh Eksepsi Ketiga
Program berikut mencoba membagi elemen-elemen suatu array satu sama lain. Jika pembagian-oleh-nol terjadi, maka eksepsi DivideByZeroException dibangkitkan. Di dalam program, eksepsi ini ditangani dengan cara melaporkan tentang terjadinya error dan kemudian melanjutkan eksekusi. Jadi, percobaan untuk melakukan pembagian-oleh-nol tidak menyebabkan program berhenti secara abnormal.

// Menangani error pembagian-dengan-nol.
using System;

class DemoEksp3
{
    static void Main()
    {
        int[] pembilang = { 4, 8, 16, 32, 64, 128 };
        int[] penyebut = { 2, 0, 4, 4, 0, 8 };
       
        for (int i = 0; i < pembilang.Length; i++)
        {
            try
            {
                Console.WriteLine(pembilang[i] + " / " +
                                  penyebut[i] + " adalah " +
                                  pembilang[i] / penyebut[i]);
            }

            catch (DivideByZeroException)
            {
                // Menangkap eksepsi.
                Console.WriteLine("Pembagian tidak dapat dilakukan!");
            }
        }
    }
}

Keluaran program ditampilkan di sini:

4 / 2 adalah 2
Pembagian tidak dapat dilakukan!
16 / 4 adalah 4
32 / 4 adalah 8
Pembagian tidak dapat dilakukan!
128 / 8 adalah 16


Menggunakan Klausa catch Jamak
Anda dapat mengasosiasikan lebih dari satu klausa catch dengan sebuah try dan hal ini sangat umum dijumpai. Namun, setiap catch harus menangkap tipe eksepsi yang berbeda. Sebagai contoh, program berikut menangkap eksepsi error batas-array dan error pembagian-oleh-nol:

// Menggunakan beberapa klausa catch.
using System;

class DemoEksp4
{
    static void Main()
    {
        // Di sini, pembilang lebih panjang dari penyebut.
        int[] pembilang = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int[] penyebut = { 2, 0, 4, 4, 0, 8 };
       
        for (int i = 0; i < pembilang.Length; i++)
        {
            try
            {
                Console.WriteLine(pembilang[i] + " / " +
                                  penyebut[i] + " adalah " +
                                  pembilang[i] / penyebut[i]);
            }

            catch (DivideByZeroException)
            {
                // Menangkap eksepsi.
                Console.WriteLine("Pembagian tidak dapat dilakukan!");
            }

            catch (IndexOutOfRangeException)
            {
                Console.WriteLine("Tidak ada elemen pembagi.");
            }
        }
    }
}

Keluaran program ditampilkan di sini:

4 / 2 adalah 2
Pembagian tidak dapat dilakukan!
16 / 4 adalah 4
32 / 4 adalah 8
Pembagian tidak dapat dilakukan!
128 / 8 adalah 16
Tidak ada elemen pembagi.
Tidak ada elemen pembagi.


Menangkap Semua Eksepsi
Kadangkala, Anda ingin menangkap semua eksepsi, apapun tipenya. Untuk melakukannya, digunakan klausa catch yang tidak menspesifikasi tipe eksepsi atau variabel eksepsi, dengan bentuk umum:

catch {
   // menangani eksepsi
}

Ini menciptakan sebuah handler “penangkap semua eksepsi” yang memastikan bahwa semua eksepsi ditangkap oleh program Anda.

Berikut adalah sebuah program yang mendemonstrasikan handler eksepsi “penangkap semua”. Perhatikan bahwa program menangkap eksepsi IndexOutOfRangeException dan DivideByZero-Exception yang dibangkitkan oleh program:

// Menangkap semua eksepsi.
using System;

class DemoEksp5
{
    static void Main()
    {
        // Di sini, pembilang lebih panjang dari penyebut.
        int[] pembilang = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int[] penyebut = { 2, 0, 4, 4, 0, 8 };
       
        for (int i = 0; i < pembilang.Length; i++)
        {
            try
            {
                Console.WriteLine(pembilang[i] + " / " +
                                  penyebut[i] + " adalah " +
                                  pembilang[i] / penyebut[i]);
            }

            catch
            { // Menangkap semua eksepsi.
                Console.WriteLine("Suatu eksepsi terjadi.");
            }
        }
    }
}

Keluaran program ditampilkan di sini:

4 / 2 adalah 2
Suatu eksepsi terjadi.
16 / 4 adalah 4
32 / 4 adalah 8
Suatu eksepsi terjadi.
128 / 8 adalah 16
Suatu eksepsi terjadi.
Suatu eksepsi terjadi.


Blok try Bersarang
Satu blok try dapat dibuat bersarang di dalam blok try lain. Eksepsi yang dibangkitkan di dalam blok try sebelah dalam yang tidak ditangkap oleh klausa catch yang berasosiasi dengan try tersebut akan dipropagasikan ke blok try sebelah luar. Sebagai contoh, di sini IndexOutOfRange-Exception tidak ditangkap oleh blok try sebelah dalam, tetapi ditangkap oleh try sebelah luar.

// Menggunakan blok try bersarang.
using System;

class CobaTryBersarang
{
    static void Main()
    {
        // Di sini, pembilang lebih panjang dari penyebut.
        int[] pembilang = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int[] penyebut = { 2, 0, 4, 4, 0, 8 };

        try { // try sebelah luar
            for (int i = 0; i < pembilang.Length; i++)
            {
                try  // try bersarang
                {
                    Console.WriteLine(pembilang[i] + " / " +
                                      penyebut[i] + " adalah " +
                                      pembilang[i] / penyebut[i]);
                }

                catch (DivideByZeroException)
                {
                    Console.WriteLine("Tidak dapat membagi dengan nol!");
                }
            }
        }
       
        catch (IndexOutOfRangeException)
        {
            Console.WriteLine("Tidak ada pembagi yang ditemukan.");
            Console.WriteLine("Error fatal -- program dihentikan.");
        }
    }
}

Keluaran program ditampilkan di sini:

4 / 2 adalah 2
Tidak dapat membagi dengan nol!
16 / 4 adalah 4
32 / 4 adalah 8
Tidak dapat membagi dengan nol!
128 / 8 adalah 16
Tidak ada pembagi yang ditemukan.
Error fatal -- program dihentikan.


Melempar Eksepsi
Beberapa contoh terdahulu menangkap eksepsi yang dibangkitkan secara otomatis oleh sistem. Namun, Anda dimungkinkan untuk melempar eksepsi secara manual menggunakan statemen throw. Bentuk umumnya adalah sebagai berikut:

throw ekspesiOb;

eksepsiOb herus berupa objek sebuah kelas eksepsi yang diderivasi dari Exception. Berikut adalah sebuah contoh yang mengilustrasikan statemen throw dengan melemparkan secara manual sebuah DivideByZeroException:

// Melempar eksepsi secara manual.
using System;

class DemoLempar
{
    static void Main()
    {
        try
        {
            Console.WriteLine("Sebelum throw.");
            throw new DivideByZeroException();
        }
       
        catch (DivideByZeroException)
        {
            Console.WriteLine("Eksepsi ditangkap.");
        }
        Console.WriteLine("Setelah statemen try/catch.");
    }
}
Keluaran program ditampilkan di sini:

Sebelum throw.
Eksepsi ditangkap.
Setelah statemen try/catch.

Perhatikan bagaimana DivideByZeroException diciptakan menggunakan new di dalam statemen throw. Ingat, throw melempar sebuah objek. Pada kasus ini, konstruktor default dipakai untuk menciptakan sebuah objek DivideByZeroException, tetapi konstruktor lain masih tersedia untuk eksepsi lain.


Melempar-Ulang Eksepsi
Eksepsi yang ditangkap oleh suatu catch dapat dilemparkan kembali sehingga dapat ditangkap oleh catch sebelah luar. Alasan yang paling sering dijumpai untuk pelemparan-ulang eksepsi adalah untuk membolehkan akses handler jamak terhadap eksepsi tersebut. Untuk melempar-ulang eksepsi, Anda hanya perlu menspesifikasi throw, tanpa menspesifikasi ekspresi. Anda bisa menggunakan format throw ini:

throw ;

Ingat, ketka Anda melempar-ulang eksepsi, ia tidak akan ditangkap oleh klausa catch yang sama. Tetapi, eksepsi tersebut akan ditangkap oleh catch sebelah luar.

Program berikut mengilustrasikan pelemparan-ulang sebuah eksepsi. Pada kasus ini, program melempar-ulang IndexOutOfRangeException.

// Melempar-ulang sebuah eksepsi.
using System;

class LemparUlang
{
    public static void BangkitEksepsi()
    {
        // Di sini, pembilang lebih panjang dari penyebut.
        int[] pembilang = { 4, 8, 16, 32, 64, 128, 256, 512 };
        int[] penyebut = { 2, 0, 4, 4, 0, 8 };

        for (int i = 0; i < pembilang.Length; i++)
        {
            try
            {
                Console.WriteLine(pembilang[i] + " / " +
                                  penyebut[i] + " adalah " +
                                  pembilang[i] / penyebut[i]);
            }

            catch (DivideByZeroException)
            {
                Console.WriteLine("Tidak bisa membagi dengan nol!");
            }
           
            catch (IndexOutOfRangeException)
            {
                Console.WriteLine("Pembagi tidak ditemukan.");
                throw; // melempar-ulang eksepsi
            }
        }
    }
}

class DemoLemparUlang {
  static void Main() {
    try {
      LemparUlang.BangkitEksepsi();
    }

    catch (IndexOutOfRangeException)
    {
      // menangkap-ulang eksepsi
      Console.WriteLine("Error fatal -- " + "program dihentikan.");
    }
  }
}

Pada program ini, error pembagian-oleh-nol ditangani secara lokal, oleh BangkitEksepsi(), tetapi error batas array dilempar-ulang. Pada kasus ini, IndexOutOfRangeException ditangani oleh Main().


Menggunakan finally
Untuk menspesifikasi sebuah blok kode yang akan dieksekusi ketika blok try/catch, Anda perlu mencantumkan blok finally di akhir runtun try/catch. Bentuk umum dari sebuah try/catch yang menyertakan finally ditunjukkan di sini:

try {
   // blok kode untuk memonitor error
}

catch (TipeEksepsi1 eksOb) {
   // handler untuk TipeEksepsi1
}

catch (TipeEksepsi2 eksOb) {
  // handler untuk TipeEksepsi2
}
.
.
.
finally {
   // kode finally
}

Blok finally akan dieksekusi ketika kendali program meninggalkan blok try/catch, apapun kondisi yang menyebabkannya. Apakah blok try berakhir secara normal atau karena eksepsi, kode yang didefinisikan di dalam finally akan dieksekusi. Berikut adalah sebuah contoh finally:

// Menggunakan finally.
using System;

class GunakanFinally {
  public static void BangkitEksepsi(int apa) {
    int t;
    int[] angka = new int[2];

    Console.WriteLine("Menerima " + apa);
   
    try
    {
        switch (apa)
        {
            case 0:
                t = 10 / apa; // membangkitkan error pemb-oleh-nol
                break;
            case 1:
                angka[4] = 4; // membangkitkan error indeks array
                break;
            case 2:
                return; // keluar dari blok try
        }
    }
   
    catch (DivideByZeroException)
    {
        Console.WriteLine("Tidak dapat membagi dengan nol!");
        return; // keluar dari catch
    }

    catch (IndexOutOfRangeException)
    {
        Console.WriteLine("Tidak ada pembagi ditemukan.");
    }
   
    finally
    {
        Console.WriteLine("Meninggalkan try.");
    }
  }
}

class DemoFinally
{
    static void Main()
    {
        for (int i = 0; i < 3; i++)
        {
            GunakanFinally.BangkitEksepsi(i);
            Console.WriteLine();
        }
    }
}

Berikut adalah keluaran program yang dihasilkan:

Menerima 0
Tidak dapat membagi dengan nol!
Meninggalkan try.

Menerima 1
Tidak ada pembagi ditemukan.
Meninggalkan try.

Menerima 2
Meninggalkan try.


Menengok Kelas Exception
Sampai titik ini, Anda telah menangkap beberapa eksepsi, tetapi belum melakukan apapun yang berkaitan dengan objek eksepsi itu sendiri. Seperti dijelaskan sebelumnya, klausa catch mengijinkan Anda untuk menspesifikasi tipe eksepsi dan variabel eksepsi. Variabel eksepsi menerima sebuah referensi yang menunjuk ke objek eksepsi. Karena semua eksepsi diderivasi dari kelas Exception, semua eksepsi mendukung semua anggota yang didefinisikan oleh Exception. Berikut akan didiskusikan beberapa anggota dan konstruktor yang umum dijumpai.

Exception mendefinisikan beberapa properti. Tiga properti yang paling menarik adalah Message, StackTrace, dan TargetSite. Ketiganya read-only. Message memuat sebuah string yang mendeskripsikan keadaan error. StackTrace memuat sebuah string yang memuat tumpukan pemanggilan yang mengarah ke eksepsi. TargetSite memuat sebuah objek yang menspesifikasi metode yang membangkitkan eksepsi.

Exception juga mendefinisikan beberapa metode. Salah satunya adalah ToString(), yang menghasilkan nilai balik berupa sebuah string yang mendeskripsikan eksepsi. ToString() secara otomatis dipanggil ketika sebuah eksepsi ditampilkan menggunakan WriteLine(), misalnya. Program berikut mendemonstrasikan ketiga properti dan metode ini.

// Menggunakan anggota-anggota Exception.
using System;

class TestEksp
{
    public static void BangkitEksepsi()
    {
        int[] angka = new int[4];
        Console.WriteLine("Sebelum eksepsi dibangkitkan.");

        // Membangkitkan sebuah eksepsi indeks-di-luar-batas.
        for (int i = 0; i < 10; i++)
        {
            angka[i] = i;
            Console.WriteLine("angka[{0}]: {1}", i, angka[i]);
        }
        Console.WriteLine("ini tidak akan ditampilkan");
    }
}

class MenggunakanEksp {
    static void Main() {
        try {
            TestEksp.BangkitEksepsi();
        }

        catch (IndexOutOfRangeException eksp) {
            Console.WriteLine("Pesan standar adalah: ");
            Console.WriteLine(eksp); // memanggil ToString()
            Console.WriteLine("Jejak tumpukan: " + eksp.StackTrace);
            Console.WriteLine("Pesan: " + eksp.Message);
            Console.WriteLine("TargetSite: " + eksp.TargetSite);
        }

        Console.WriteLine("Setelah blok catch.");
    }
}

Keluaran program ditampilkan di sini:

Sebelum eksepsi dibangkitkan.
angka[0]: 0
angka[1]: 1
angka[2]: 2
angka[3]: 3
Pesan standar adalah:
System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at TestEksp.BangkitEksepsi() in E...\TestEksp.cs:line 14
   at MenggunakanEksp.Main() in E:\...\TestEksp.cs:line 24

Jejak tumpukan:    at TestEksp.BangkitEksepsi() in E:\...\TestEksp.cs:line 14
   at MenggunakanEksp.Main() in E:\...\ TestEksp.cs:line 24

Pesan: Index was outside the bounds of the array.
TargetSite: Void BangkitEksepsi()

Setelah blok catch.


Eksepsi Referensi null
Eksepsi penting lainnya adalah NullReferenceException. Eksepsi ini dilemparkan ketika terdapat percobaan untuk menggunakan referensi null untuk menunjuk sebuah objek, misalnya, jika Anda mencoba untuk memanggil sebuah metode manggunakan referensi null. Referensi null adalah sebuah referensi yang tidak menunjuk ke sembarang objek. Salah satu cara untuk menciptakan sebuah referensi null adalah dengan secara eksplisit menugaskan nilai null kepada sebuah referensi menggunakan katakunci null. Berikut adalah sebuah program yang mendemonstrasikan NullReferenceException:

// Menggunakan NullReferenceException.
using System;

class X
{
    int x;
   
    public X(int a)
    {
        x = a;
    }
   
    public int Tambah(X o)
    {
        return x + o.x;
    }
}

// Demonstrasi NullReferenceException.
class DemoNRE {
    static void Main() {
        X p = new X(10);
        X q = null; // q secara eksplisit ditugasi null
        int nil;

        try
        {
            nil = p.Tambah(q); // ini akan menyebabkan eksepsi
        }

        catch (NullReferenceException)
        {
            Console.WriteLine("NullReferenceException!");
            Console.WriteLine("memperbaiki...\n");
   
            // Sekarang, diperbaiki.
            q = new X(9);
            nil = p.Tambah(q);
        }

        Console.WriteLine("nil adalah {0}", nil);
    }
}

Keluaran program ditampilkan di sini:

NullReferenceException!
memperbaiki...

nil adalah 19

Program menciptakan sebuah kelas, bernama X, yang mendefinisikan sebuah anggota, x, dan metode Tambah(), yang menjumlahkan objek pemanggil x dan x yang dilewatkan sebagai parameter. Di dalam Main(), dua objek X diciptakan. Pertama, p diinisialisasi. Kedua, q tidak diinisialisasi, tetapi ditugasi null secara eksplisit. Kemudian, p.Tambah() dipanggil dengan q sebagai argumen. Karena q tidak menunjuk ke objek apapun, NullReferenceException dibangkitkan ketika percobaan untuk mendapatkan nilai dari q.x dilakukan.


Mewarisi Kelas Exception
Meskipun dapat menangani kebanyakan eksepsi yang terjadi, mekanisme penanganan eksepsi C# tidak dibatasi hanya untuk mengatasi masalah-masalah built-in. Kekuatan dari penanganan eksepsi C# adalah kemampuannya untuk menangani tipe eksepsi yang Anda ciptakan sendiri. Penciptaan sebuah eksepsi adalah pekerjaan mudah, Anda hanya perlu mendefinisikan sebuah kelas yang mewarisi Exception. Kelas terderivasi Anda tidak perlu mengimplementasi apapun.

Kelas eksespi yang Anda ciptakan akan secara otomatis memiliki properti dan metode yang didefinisikan oleh Exception. Anda bisa mendefinisikan-ulang (override) satu atau lebih anggota tersebut di dalam kelas eksepsi yang Anda ciptakan.



Ketika menciptakan kelas eksepsi Anda sendiri, Anda secara umum menginginkan kelas Anda untuk mendukung semua konstruktor yang didefinisikan oleh Exception. Untuk kelas eksepsi sederhana, hal ini mudah dilakukan karena Anda hanya perlu melewatkan argumen konstruktor kepada konstruktor Exception terkait melalui base. Anda hanya perlu menyediakan konstruktor yang secara aktual dipakai di dalam program Anda.

Berikut adalah sebuah contoh yang memanfaatkan tipe kelas sendiri. Di akhir Bab 9, sebuah kelas array, RentangArray, telah dikembangkan. Seperti yang Anda ingat, RentangArray mendukung array int satu dimensi dimana di dalamnya indeks awal dan indeks akhir dispesifikasi oleh pengguna. Sebagai contoh, sebuah array dengan rentang dari -5 sampai 27 merupakan hal legal untuk RentangArray. Pada Bab 9, jika sebuah indeks di luar rentang, variabel error khusus didefinisikan oleh RentangArray ditetapkan (diberi nilai). Ini berarti bahwa variabel error harus diperiksa setiap kali operasi dilakukan oleh kode yang menggunakan RentangArray. Pendekatan semacam itu tentu saja tidak cukup handal. Pendekatan lebih baik yang bisa dilakukan adalah dengan meminta RentangArray untuk melemparkan eksepsi ketika error rentang terjadi.

// Menggunakan Exception untuk error RentangArray.
using System;

// Menciptakan eksepsi RentangArray.
class EksepsiRentangArray : Exception
{
    /* Mengimplementasikan semua konstruktor Exception. Perhatikan bahwa
       konstruktor hanya mengeksekusi konstruktor kelas basis.
       Karena EksepsiRentangArray tidak menambahkan apapun pada Exception,
       tidak perlu aksi apapun yang dilakukan. */

    public EksepsiRentangArray() : base() { }
    public EksepsiRentangArray(string pesan) : base(pesan) { }
   
    public EksepsiRentangArray(string pesan, Exception eksepsiDalam) :
        base(pesan, eksepsiDalam) { }

    protected EksepsiRentangArray(
        System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext konteks) :
        base(info, konteks) { }

    // Mendefinisikan-ulang ToString untuk EksepsiRentangArray.
    public override string ToString()
    {
        return Message;
    }
}

// Versi terperbaiki dari RentangArray
class RentangArray
{
    // data private.
    int[] a; // referensi ke array

    int batasBawah; // indeks terkecil
    int batasAtas; // indeks terbesar

    // properti Length, read-only.
    public int Length { get; private set; }

   // menciptakan array dengan ukuran tertentu.
    public RentangArray(int bawah, int atas)
    {
        atas++;
        if (atas <= bawah)
        {
            throw new EksepsiRentangArray("Indeks bawah tidak kurang dari indeks atas.");
        }

        a = new int[atas - bawah];
        Length = atas - bawah;
        batasBawah = bawah;
        batasAtas = --atas;
    }

    // Ini adalah indekser untuk RentangArray.
    public int this[int indeks]
    {
        // Ini adalah aksesor get.
        get
        {
            if (ok(indeks))
            {
                return a[indeks - batasBawah];
            }

            else {
                throw new EksepsiRentangArray("Error rentang.");
            }
        }

        // Ini adalah aksesor set.
        set
        {
            if (ok(indeks))
            {
                a[indeks - batasBawah] = value;
            }
            else throw new EksepsiRentangArray("Error rentang.");
        }
    }

    // Menghasilkan true jika indeks di dalam batas.
    private bool ok(int indeks)
    {
        if (indeks >= batasBawah & indeks <= batasAtas) return true;
        return false;
    }
}

// Demonstrasi array rentang-indeks.
class DemoRentangArray
{
    static void Main()
    {
        try {
            RentangArray ra = new RentangArray(-5, 5);
            RentangArray ra2 = new RentangArray(1, 10);
          
            // Demonstrasi ra.
            Console.WriteLine("Panjang dari ra: " + ra.Length);

            for (int i = -5; i <= 5; i++)
              ra[i] = i;

            Console.Write("Isi dari ra: ");
            for (int i = -5; i <= 5; i++)
              Console.Write(ra[i] + " ");

            Console.WriteLine("\n");

            // Demonstrasi ra2.
            Console.WriteLine("Panjang dari ra2: " + ra2.Length);

            for (int i = 1; i <= 10; i++)
              ra2[i] = i;

            Console.Write("Isi dari ra2: ");

            for (int i = 1; i <= 10; i++)
                Console.Write(ra2[i] + " ");

            Console.WriteLine("\n");
        }
       
        catch (EksepsiRentangArray eksp) {
            Console.WriteLine(eksp);
        }

        // Sekarang, demonstrasi beberapa error.
        Console.WriteLine("Sekarang membangkitkan beberapa error.");
       
        // Menggunakan konstruktor tak-valid.
        try
        {
            RentangArray ra3 = new RentangArray(100, -10); // Error
        }

        catch (EksepsiRentangArray eksp)
        {
            Console.WriteLine(eksp);
        }
       
        // Menggunakan indeks tak-valid.
        try
        {
            RentangArray ra3 = new RentangArray(-2, 2);
           
            for (int i = -2; i <= 2; i++)
                ra3[i] = i;
           
            Console.Write("Isi dari ra3: ");
           
            for (int i = -2; i <= 10; i++) // membangkitkan error rentang
                Console.Write(ra3[i] + " ");
        }

        catch (EksepsiRentangArray eksp) {
            Console.WriteLine(eksp);
        }
    }
}

Keluaran program ditampilkan di sini:

Panjang dari ra: 11
Isi dari ra: -5 -4 -3 -2 -1 0 1 2 3 4 5

Panjang dari ra2: 10
Isi dari ra2: 1 2 3 4 5 6 7 8 9 10

Sekarang membangkitkan beberapa error.
Indeks bawah tidak kurang dari indeks atas.
Isi dari ra3: -2 -1 0 1 2 Error rentang.








No comments:

Post a Comment