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