Hata Yakalamak ve Exception Sınıfları (2. Bölüm)

Aynı başlıklı bu dersimize ikinci bölüm ile devam ediyoruz.

Önceki derste sözünü ettiğimiz sınıflardan başka .NET Framework ile birlikte OverflowException, InvalidCastException, FormatException, ArgumentException, ArithmeticException, OutOfMemoryException, ArrayTypeMismatchException NullReferansException ve MemberAccessException gibi sınıflar da gelmektedir.

En çok kullanılan hata yakalama sınıflarından böylece biraz söz ettikten sonra mevcut Exception sınıflarıyla entegre hata ayıklayıcının ilişkisinden biraz söz edelim. Bu amaçla Debug menüsünden komut verip ekrana Exceptions diyalog kutusunu getirdim.



Bu diyalog kutusunda C# uygulamaları dahilinde yararlanabileceğiniz Exception sınıfları ile entegre hata ayıklayıcısı arasındaki ilişki ayarlanmaktadır. Bu konuda bilgi vermek için forma bir TextBox ve düğme yerleştirip "islem" adını verdiğim düğme için aşağıda verdiğim kodu hazırladım.


private void islem_Click(object sender, EventArgs e)

  {

    byte sayi;

    try

     {

       sayi = Convert.ToByte(textBox1.Text);    

     }

   catch 

    {

      MessageBox.Show("Hata meydana geldi");  

    }

 }



Tahmin edeceğiniz gibi Debug modunda iken uygulama Start Debugging komutu ile çalıştırılıp formdaki ilk TextBox'a 255'ten büyük bir değer girilmesi halinde hata meydana gelir ve OverflowException sınıfının örneği hazırlanıp catch bloğuna gönderilir.

Şimdi öyle bir ayarlama yapacağım ki, uygulama Debug modunda iken Start Debuging komutu ile çalıştırıldığında catch bloğundaki satırların işletilmesi yerine uygulamanın çalışmasının kırılmasını sağlayacağım. Bu amaçla Exceptions diyalog kutusunda önce Common Language Runtime Exceptions adlı exception sınıfı grubuna ait namespace'lerin listelenmesini sağladım. Ardından System'de yer alan OverflowException sınıfını buldum.



Verilen ekran görüntüsünde tespit edebileceğiniz gibi Thrown özelliği pasif iken User-unchandled özelliği aktif durumdadır. Başka bir deyişle kodun herhangi bir yerinde meydana gelebilecek ve OverflowException sınıfının fırlatılmasına neden olan hata yakalanmazsa programın çalışması kırılır. Yukarıda verdiğim kodda TextBox'a 255'ten büyük bir değer girilirse, dolayısıyla byte tipindeki değişkene 255'ten büyük bir sayı aktarılmak istenirse meydana gelebilecek hata yakalandığı için uygulamanın çalışması kırılmaz. Başka bir deyişle mevcut ayarlarda byte değişkene 255'ten büyük bir değer aktarılmak istenirse ve OverflowException hatasını yakalayan bir catch bloğu yoksa uygulamanın çalışması kırılır. Benzer şekilde .NET Framework ile gelen sınıfların hemen hepsi için User-unhanded sütunundaki onay kutusu seçili durumda olduğundan yakalanmayan bütün hatalar uygulama Debug modunda iken çalışmanın kırılmasına neden olurlar.

Bu diyalog kutusunda System'de yer alan OverflowException sınıfının User-unchandled özelliğini aktif durumda iken ayrıca Thrown özelliğini aktif duruma getirdikten sonra OK düğmesi ile bu diyalog kutusunu kapattım. Ardından yukarıda verdiğim kodu aşağıdaki gibi düzenledim. Değişken adı benzerliği olmasın diye "sayi" yerine "number" adını kullandım.


private void islem_Click(object sender, EventArgs e)

  {

    byte number;

    try

     {

       number = Convert.ToByte(textBox1.Text);

       if (number == 100)

          throw new OverflowException(); 

     }

   catch 

    {

      MessageBox.Show("Hata meydana geldi veya 100 sayısını girdiniz");  

    }

  }



Önce bu kodu biraz yorumlayalım: TextBox'a 255'ten büyük bir değer girilip bu kod işletilirse ToByte() metodunun kullanıldığı satır hataya neden olur ve catch bloğuna OverflowException sınıfının örneği gönderilir. TextBox'a 100 sayısı girilip bu kod işletildiğinde ise try bloğundaki karşılaştırma doğru değerini vereceği için catch bloğuna yine OverflowException hata sınıfının örneği gönderilir. Tekrar etmek gerekirse ister TextBox'a 100 ister 255'ten büyük bir sayı yazılsın OverflowException sınıfının örneği catch bloğuna fırlatılır.

Bu şartlarda yani Exceptions diyalog kutusunda OverflowException sınıfının Thrown özelliği seçili durumda iken Debug modundaki uygulama Start Debugging komutu ile çalıştırıldığında TextBox'a ister 100 girilsin ister 255'ten büyük sayı girilsin uygulamanın çalışması kırılır. Çünkü Exception diyalog kutusunda yaptığım ayarlamanın anlamı şudur: Her ne nedenden olursa olsun ister direk Throw deyimi ile, ister yapılmak istenen işlem OverflowException hatasını üretirse uygulamanın çalışması kırılır.

Tabi uygulamayı Build menüsündeki komutlarla derleyip sonra klasik yöntemlerle(yani EXE dosyanın olduğu klasöre gidip EXE dosyayı çift tıklayarak) Visual Studio'dan bağımsız olarak çalıştırırsanız TextBox'a ister 255'ten büyük sayı girilsin ister 100 girilsin catch bloğundaki satırlar işletilir ve uygulamanın çalışması kırılmaz ve catch bloğu işletilerek hata yakalanır.

Finally Bloğu

Bazen herhangi bir nedenden dolayı yarı yolda vazgeçilen bir işlemden dolayı geride bazı kalıntılar kalır. Bu gibi durumlar kaynak israfına neden olur. Bu konuda bilgi vermek için aşağıda verdiğim ancak pratik değeri olmayan uygulamayı hazırladım.



Kullanıcı bir resmin dosyasının içeriğini görmek istediğinde 'Resim Seç' düğmesini tıklayacak. Bu düğme tıklandığında Aç diyalog kutusu ekrana getirile­cek ve istediği resim dosyasını seçecek. Bu işlemler için aşağıda verdiğim kodu hazırladım. Kullanıcı Aç diyalog kutusunda PictureBox'ta görüntü­lenebilecek uygun bir resim dosyasını seçtiği sürece bu kod hatasızca çalışır.

1994 yılında yayınlanan Clipper kitabımın birçok sayfasında 'DosyaAdi' adında değişken tanımlamışım. 2004 yılında yayınlanan C# kitabımda ise 'dosya' adında değişkenler tanımlamışım. Doğrusunu söylemek gerekirse kısacak kodları hazırlarken değişkenin adı şu olsun bu olmasın diye kaygım yıllardır olmadı. Değişkene ad seçerken genellikle değişkene aktaracağım bilgiyi göre seçim yaparım. Örneğin tanımladığım değişkene Fenerbahçe bilgisini aktarmayı düşünüyorsam değişkene genellikle 'takim' adını veririm. Yıllar önce yani 1990'lı yılların başında programcılık kitabı yazarlarının ve hocaların arasında 'dosya' yerine 'kutuk' adında değişken tanımlama alışkanlığı yaygındı. Sonra 'kutuk' unutuldu ve 'dosya' diyenler çoğaldı. Gerçekte programcılık sitelerindeki makalelerde ve kitaplarda 'File' karşılığı 'dosya' adında değişken tanımlama alışkanlığı çok yaygındır.


private void Resim_sec_Click(object sender, EventArgs e)

 {

   openFileDialog1.ShowDialog();

   string kutuk = openFileDialog1.FileName;

   Bitmap Resim = new System.Drawing.Bitmap(kutuk);

   pictureBox1.Image = (Bitmap)Resim;

   Resim = null;      

 }



Ancak kullanıcı OpenFileDialog nesnesi sayesinde ekrana getirilen diyalog kutusunu dosya seçmeden kapatır veya uygun olmayan bir dosyayı seçerse hata meydana gelir. Kullanıcının yanlış yaptığı veya yapmadığı dosya seçiminden dolayı meydana gelecek hata konusunda kendisine bilgi vermek için kodu aşağıdaki gibi düzenledim.


private void Resim_sec_Click(object sender, EventArgs e)

 {

   System.Drawing.Bitmap Resim;

   openFileDialog1.ShowDialog();

   string kutuk = openFileDialog1.FileName;

   try

    {

      Resim = new System.Drawing.Bitmap(kutuk);

      pictureBox1.Image = (Bitmap)Resim;

      Resim = null;      

    }

  catch 

   {

     MessageBox.Show("Uygun dosya seçmediniz");

   }

 }



Kullanıcı 'Aç' diyalog kutusunda resim dosyası seçmezse veya uygun olmayan bir dosyayı seçerse catch bloğundaki satır işletilir ve kullanıcıya bu konuda bilgi verilir. Ancak bu durumda Bitmap tipindeki nesne bellekte yaşamaya devam eder. Çünkü 'Resim' adını vermiş olduğum Bitmap nesnesini null yapan satırı try bloğuna yazmıştım.

Hata meydana gelsin veya gelmesin mutlaka işletilmesini istediğiniz satırlar varsa bu satırları finally bloğuna yazmalısınız. finally bloğunun nasıl kullanıldığını anlatmak için yukarıda verdiğim kodu aşağıdaki gibi değiştirdim. Bitmap tipindeki değişkeni blok içinde tanımladı­ğım için Garbage Collector zaten bir süre sonra devreye girip bu nesneyi bellekten temizler ama buradaki amacımız deneysel.


private void Resim_sec_Click(object sender, EventArgs e)

 {

   System.Drawing.Bitmap Resim;

   openFileDialog1.ShowDialog();

   string Dosya = openFileDialog1.FileName;

   try

    {

      Resim = new System.Drawing.Bitmap(Dosya);

      pictureBox1.Image = (Bitmap)Resim;

     }

    catch

     {

    MessageBox.Show("Uygun dosya seçmediniz");

     }

   finally

     {

        Resim = null;

     }

   }



İstenirse birden fazla catch bloğu ve/veya iç-içe try-catch bloğu hazırlayabilirsiniz. Söz konusu hatanın birden fazla nedenden kaynaklanması ihtimali varsa try bloğu için birden fazla catch bloğu anabilir. Aşağıda iç-içe try-catch bloğunun kalıbı bulunmak­tadır.


try

{

  try

   {

   }

  catch

    {

    }

  finally

   {

   }

 }

catch

 {

 }

finally

 {

 }



Hata Yakalama Sınıfları

C# projelerinde hataları yakalarken kullanabileceğiniz çok sayıda sınıf bulunmak­tadır. Yukarıdaki sayfalarda System.Exception, OverflowException, DivideByZeroException ve IndexOutOfRangeException sınıflarından kısaca söz edildi. Hata yakalama sınıfları­nın işlevleri biraz zor anlaşıldığı için yukarıda kısaca anlattığım konulardan tekrar söz edeceğim. Öncelikle aşağıda verdiğim kodu incelemenizi istiyorum.


private void Hesapla_Click(object sender, EventArgs e)

 {

   try

     {

      byte Sayi1 = Convert.ToByte(textBox1.Text);

      byte Sayi2 = Convert.ToByte(textBox2.Text);

      float Sonuc = Sayi1 / Sayi2;

      textBox3.Text = Sonuc.ToString();  

    }

   catch(System.Exception Hata) 

    {

      MessageBox.Show("Hata Meydana Geldi" + "nr" + Hata);

      textBox1.Focus();  

     }

  }



Bu metni ilk kaleme aldığımda alışkanlıktan Sayi1, Sayi2 adında değişkenler tanımladım. Başka kaynaklardan yararlanmadan değişkenleri tanımlayabildiğimi kanıtlamak için yukarıda verdiğim kodu aşağıdaki gibi bir değişiklik yaptım.


private void Hesapla_Click(object sender, EventArgs e)

 {

   try

     {

      byte Number1 = Convert.ToByte(textBox1.Text);

      byte Number2 = Convert.ToByte(textBox2.Text);

      float Netice = Number1 / Number2;

      textBox3.Text = Netice.ToString();  

    }

   catch(System.Exception Hataspor) 

    {

      MessageBox.Show("Hata Meydana Geldi" + "nr" + Hataspor);

      textBox1.Focus();  

     }

  }



2. TextBox'a sıfır(0) girilip bu kod işletilirse sıfıra bölme hatası meydana gelir(yani catch bloğuna DivideByZeroException nesnesi fırlatılır) ve meydana gelen hata ile ilgili olarak ekrana hata mesajı getirilir. Hata nedeniyle ekrana getirilen diyalog kutusundaki mesaja bakarsanız hatanın sıfıra bölmeden kaynaklandığını fark edebilirsiniz.




Bu kodda yukarıdaki sayfalarda verilen örneklerden farklı olarak 'Hataspor' adını verdiğim bir nesne var. Bildiğiniz gibi nesneleri new anahtar kelimesini kullanıp ilgili sınıfın yapıcı metodundan yararlanarak hazırlıyorduk. Ancak burada 'Hataspor' adını verdiğim System.Exception tipindeki nesne otomatik olarak hazırlandı. Bu kodda catch bloğuna DivideByZeroException nesnesi gönderilmesine ve catch deyimine ait parantezlerin içinde System.Exception tipinde değişken tanımlanmasına rağmen hata yakalandı.

Nasıl ki Object tipindeki değişkenlere istenen tipteki bilgiler aktarılabiliniyorsa bütün hata sınıflarının türetildiği Exception sınıfı tipindeki değişkene istenen hata sınıfının örneği aktarılabilinmektedir. try bloğundan catch bloğuna gönderilen DivideByZeroException nesnesi hatasızca dönüştürülüp System.Exception tipindeki değişkene aktarılabildiği için hata yakalanıp catch bloğundaki satırlar işletilebilmektedir.

Sıfıra bölme dışında başka bir hata meydana gelirse ekrana getirilen hata mesajı farklı olur. Çalışma anında TextBox'lardan birisine 255'ten büyük bir değer yazılıp bu kod işletilmiş olunsaydı aşağıdaki gibi hata mesajı ekrana getirilirdi



Şimdi yukarıda verdiğim kodun try bloğunda değişiklik yapıp sıfıra bölme hatası meydana geldiğinde başka mesaj, TextBox'a byte değişkene sığmayacak büyüklükte bir değer girildi­ğinde başka bir mesajın ekrana getirilmesini sağlayacağım. Aşağıda verilen kodu incelerseniz bu 2 catch bloğunda catch deyimlerine ait parantezle­rin arasına sınıfların adını yazmakla yetinip değişken tanımlamadım. try bloğundan gönderilen nesneyi catch bloğu içinde kullanmayacaksanız parantezlerin arasına ilgili sınıfın adını yazmanız yeterlidir.


catch(DivideByZeroException)

 {

MessageBox.Show("Sıfıra bölme hatası yaptınız");

textBox1.Focus(); 

       }

catch(OverflowException)

       {

 MessageBox.Show("Büyük sayı girdiğiniz");

 textBox1.Focus(); 

 }



Bu kodu dikkatle incelerseniz try bloğundan sonra birden fazla catch bloğu olduğunu görürsünüz. Hazırladığım örneğin catch bloklarını bu şekilde değiştirip TextBox'ların birisine karaktersel bilgi girip bu kodu işletirsem meydana gelen hata yakalanamaz ve programın çalışması kırılır. Çünkü TextBox'a karaktersel bilgi girilip bu kod işletilirse Convert sınıfının ToByte() metodunun kullanıldığı satırlar hataya neden olur.


Number1 = Convert.ToByte(textBox1.Text);

Number2 = Convert.ToByte(textBox2.Text);



Convert sınıfı ile string bilgiler byte tipine dönüştürmeye çalışılırken meydana gelebilecek hataları yakalamak istiyorsanız 3. bir catch bloğu hazırlayabilirsiniz. Convert sınıfının herhangi bir metodu ile dönüştürme yapılırken hata meydana geldiğinde catch bloğuna FormatException sınıfının örneği gönderilmektedir.

Aşağıdaki gibi 3 catch bloğu hazırlayıp hem ilk TextBox'a byte tipindeki değişkene akta­rılamayacak büyüklükte bir sayı girip hem de 2. TextBox'a 0 yazıp sıfıra bölme hatasının meydana gelmesini sağlarsanız ilk TextBox'ın içeriği byte değişkene aktarılırken hata meydana geleceği için catch bloğuna OverflowException nesnesi gönderilir ve 2. catch bloğu işletilir.


catch(DivideByZeroException)

 {

   MessageBox.Show("Sıfıra bölme hatası yaptınız");

   textBox1.Focus(); 

 }

catch(OverflowException)

 {

   MessageBox.Show("Büyük sayı girdiğiniz");

   textBox1.Focus(); 

 }

catch (FormatException)

 {

   MessageBox.Show("Yanlış türde bilgi girdiniz");

   textBox1.Focus();

 }



Hata yakalama konusuna devam etmek üzere forma bir PictureBox yerleştirdim ve aşağıda verdiğim kodu hazırladım. Bu kodda önce 'Resim' adını verdiğim Image nesnesi için değişken tanımlanmaktadır. Ardından FromFile() metodu ile ilgili BMP dosyası okunup Image nesnesi hazırlanıp bu nesnenin referansı PictureBox'a aktarılmaktadır.


System.Drawing.Image Resim;

Resim = System.Drawing.Image.FromFile("C:athena.bmp");

pictureBox1.Image = Resim;



FromFile() metoduna parametre olarak verilen dosya bulunmazsa veya içeriği Image nesnesine aktarılmaya uygun olmayan bir dosyanın adı FromFile() metoduna parametre olarak verilirse hata meydana gelir. Kodu aşağıdaki gibi düzenlemiş olsaydım hata yakalanırdı.


System.Drawing.Image Resim;

try

 {

   Resim = System.Drawing.Image.FromFile("C:athena.bmp");

   pictureBox1.Image = Resim;

 }

catch(System.IO.FileNotFoundException)  

 {

   MessageBox.Show("Dosya bulunamadı"); 

 }



Bu kodda kullandığım FileNotFoundException sınıfı şimdiye kadar hakkında bilgi verilen hata sınıflarından farklı olarak System.IO'da tanımlıdır. Bu nedenle ya bu namespace kullanılan sınıfların arandığı namespace listesine using deyimi dahil edilmeli veya namespace adı sınıf adından önce yazılmalıdır. Meydana gelmesi muhtemel olan hata ile ilgili hangi tip Exception nesnesinin oluşturulaca­ğını tahmin edemiyorsanız Exception adlı genel sınıfı kullanabilirsiniz.


System.Drawing.Image Resim;

try

 {

   Resim = System.Drawing.Image.FromFile("C:athena.bmp");

   pictureBox1.Image = Resim;

 }

catch(System.Exception)  

 {

   MessageBox.Show("Hata meydana geldi"); 

 }



Bu şartlarda hangi nedenden kaynaklanırsa kaynaklansın herhangi bir hata meydana geldi­ğinde try-catch bloğundaki catch(System.Exception) karşılaştırması doğru değeri verece­ğinden bloklanan satırlar işletilir.

Bu durumda Exception nesnesini inceleyip meydana gelen hatanın izini sürebilirsiniz. Bu konuda bilgi vermek için yukarıda verdiğim kodu aşağıdaki gibi düzenledim. Hata meydana geldiğinde bu catch bloğu sayesinde hazırlanan Exception nesnesinin hangi sınıftan türetildiği öğrenilip ona göre işlem yapılabilir.


catch(System.Exception Hata)

 {

   MessageBox.Show("Hata meydana geldi" + "nr" +

     "Orijinal Hata mesajı:"+ Convert.ToString(Hata.GetType()));

 }



Yukarıda sözü edilen sınıflardan başka OverflowException, InvalidCastException, DevideByZeroException, FormatException, ArgumentException, ArithmeticException, OutOfMemoryException, ArrayTypeMismatchException, NullReferansException ve MemberAccessException gibi sınıflar da .NET Framework ile birlikte gelmektedir.

InvalidCastException Sınıfı

Daha önceki konulardan bildiğiniz gibi değişik tipteki bilgileri başka bir tipe dönüştürmek mümkündür. Yine operatörler adlı bölümde söz edildiği gibi as ve is operatörlerin­den yararlanıp tip dönüşümleri yapılabilmektedir. Tip dönüştürme işlemleri sırasında hata meydana geldiğinde InvalidCastException hatası meydana gelmektedir. InvalidCastException sınıfını anlatmak için aşağıda verdiğim kodu hazırladım.


object Nesne = 2008;

try

 {

   int Yil = (int)Nesne; // Tehlikeli bir değişken'

   this.Text = Yil.ToString();

 }

catch (System.InvalidCastException Hataspor)

 {

   MessageBox.Show("Dönüştürme hatası meydana geldi" + 

nr" + Hataspor.Message);

 }



Bu kodda 'Nesne' adını verdiğim(yoksa felsefe ve yayıncılık kavramı olan nesnel'i mi kullansaydım!) object tipindeki değişkenin içeriği rakamlardan meydana geldiği için Unboxing işlemi yapılıp sorunsuz bir şekilde int tipindeki değişkene aktarılır. object tipindeki değişkenin içeriği int tipindeki değişkene aktarılırken Unboxing işlemi başarısız olsaydı InvalidCastException hatası ile karşılaşılırdı.

Diğer yandan null yapılmış bir nesnenin özelliklerini kullanmak isterseniz NullReferenceException hatası ile karşılaşılır. Bu hata ile nasıl karşılaşıldığını göstermek için bir Bitmap nesnesi hazırladım. Ardından bu nesneyi null yapıp Width ve Height özelliklerini kullanmayı denedim. Aşağıda verdiğim kodda 'i' ve 'j' tehlikeli değişkenler oldukları için alışkanlıklarımı bir tarafa bırakıp çift 'ii' ve çift 'jj' tercih ettim.


System.Drawing.Bitmap Resim;

openFileDialog1.ShowDialog();

string Dosya = openFileDialog1.FileName;

try

 {

   Resim = new System.Drawing.Bitmap(Dosya);

   Resim = null;

   int ii = Resim.Width;

   int jj = Resim.Height;

 }

catch(NullReferenceException) 

 {

   MessageBox.Show("Nesne null yapılmış");

 }



Throw Anahtar Kelimesi İle Exception Nesnesi Hazırlamak

Şimdiye kadar anlatılanlardan çıkarmış olabileceğiniz gibi herhangi bir nedenden dolayı çalışma anında bir hata meydana geldiğinde uygulama tarafından karşılaşılan hataya uygun olarak ilgili hata sınıfının örneği hazırlanıp catch bloğuna gönderilmektedir. Bizim yaptığı­mız, fırlatılan nesnenin hangi sınıfın örneği olduğunu öğrenip ona göre kullanıcıya mesaj verip tedbir almaktı. Burada asıl vurgulamak istediğim konu, sistem bir exception fırlat­tığında bu Exception nesnesine kaynaklık edecek sınıfı kendisinin seçtiğidir.

Şimdi kendim throw deyiminden(anahtar kelime) yararlanıp bir Exception nesnesi hazırlayacağım. Bu amaçla kullanıcıdan gün, ay ve yıl bilgilerini isteyip bunları birleştirip tarih bilgisi elde edeceğim. Bu amaçla forma 4 TextBox yerleştirip aşağıda verdiğim kodu hazırladım.


private void Hesapla_Click(object sender, EventArgs e)

 {

   DateTime Tarih; // Kendinizi garantiye almak için siz 'T' deyin

   int gun = Convert.ToInt16(textBox1.Text);

   int ay = Convert.ToInt16(textBox2.Text);

   int yil = Convert.ToInt16(textBox3.Text);

   Tarih=Microsoft.VisualBasic.DateAndTime.DateSerial(yil,ay, gun);

   textBox4.Text = Tarih.ToShortDateString();

 }



Kullanıcı TextBox'lara uygun değerleri girdiğinde bu kod sorunsuz çalışır. Ancak gün bilgisi olarak 31'den, ay bilgisi olarak 12'den büyük bir değer girdiğinde gün, ay ve yıl bilgileri birleştirilip tarih elde edilmek istendiğinde hata meydana gelir. Yanlış bilgi girişinden dolayı programın kırılmasını önlemek için yukarıda yapıldığı gibi try-catch bloğu hazırlayabilir veya kullanıcının girdiği bilgileri DateSerial() metoduna parametre olarak vermeden önce kendiniz kontrol edebilirsiniz. Pratik değeri olmasa bile aşağıda verdi­ğim kod hatasız çalışır ve DateSerial() metoduna parametre olarak verilecek ay ve gün bilgilerinin yanlış olması engellenir.


private void Hesapla_Click(object sender, EventArgs e)

 {

   DateTime History;

   int gun = Convert.ToInt16(textBox1.Text);

   int ay = Convert.ToInt16(textBox2.Text);

   int yil = Convert.ToInt16(textBox3.Text);

   if (gun > 31 || ay > 12)

     try

      {

        throw new OverflowException("Yanlış bilgi girdiniz");

      }

     catch (Exception Hataspor)

      {

       MessageBox.Show(Hataspor.Message);

       textBox1.Focus();  

      }

   History= Microsoft.VisualBasic.DateAndTime.DateSerial(yil, ay, gun);

   textBox4.Text = History.ToShortDateString();

 }



Kullanıcı ay veya gün bilgilerinde sınırı aştığında catch bloğu işletilir. try bloğunda şimdiye kadar yapılanlardan farklı olarak throw deyimi ile OverflowException nesnesi hazırlanmaktadır. Burada throw deyimi ile Exception nesnesi hazırlandığı için sıra catch bloğunu işletmeye gelir.

catch bloğunda ise kullanıcıya mesaj verildikten sonra programın işletimi başa alınıp kullanıcıya girdiği bilgileri düzeltme şansı verilir. throw deyimi ile OverflowException nesnesi yerine başka bir Exception nesnesi hazırlayabilirdim. Hemen hatırlatmak gerekirse, throw anahtar kelimesiyle ilgili exception sınıfının örneğini alma işlemine daha çok kendi özel hata sınıflarını hazırlayan programcılar gerek duymaktadır.

Şimdi throw deyimi ile ilgili 2. bir örnek vereceğim. Bu amaçla Bol() adında ve float tipinde 2 parametreye sahip bir metot hazırladım. Bu metot ile birbirine bölünecek sayılar TextBox'lardan alınacaktır. Sıfıra bölme hatası ile ilgili metottaki 'sayi1' ve 'sayi2' adındaki parametreleri yani değişkenleri sonradan 's1' ve 's2' olarak değiştirdim.


private void Hesapla_Click(object sender, EventArgs e)

 {

   float bolunen, bolen, sonuc;

   bolunen = Convert.ToSingle(textBox1.Text);

   bolen = Convert.ToSingle(textBox2.Text);

   if (bolen == 0)

    try

     {

      throw new DivideByZeroException("Sıfıra bölme hatası");

     }

    catch (DivideByZeroException Hata)

     {

       MessageBox.Show(Hata.Message);

       textBox2.Focus();

     }

    else

     {

      sonuc = bol(bolunen, bolen);

      textBox3.Text = sonuc.ToString();

    }

   }

 float bol(float s1, float s2)

  {

    return s1 / s2; 

  }



İlk 2 TextBox'a yazılan bilgileri float tipindeki değişkenlere aktardıktan sonra bölme işlemini yapmadan 2. sayının yani bölenin 0 olup olmadığını araştırdım. İkinci sayı 0 ise programın işletiminin try-catch bloğuna geçmesini sağlayıp throw anahtar kelimesi ile DivideByZeroException sınıfının örneğini hazırladım.

Tabii bu kodda TextBox'lara girilen bilgiler Convert sınıfının ToSingle() metodu ile float tipe dönüştürülmeye uygun değilse başka hatalar meydana gelir. Hazırladığım bu kodda catch bloğunda farklı hata bir sınıfının adını yazsaydım veya DivideByZeroException dışında başka bir sınıfın örneğini hazırlasaydım sıfıra bölme hatası yakalanamazdı. Şimdi farklılık olsun diye try-catch bloğu ile throw deyiminin farklı yerlerde olmasını sağlayacağım.


private void Hesapla_Click(object sender, EventArgs e)

 {

   float bolunen, bolen, sonuc;

   bolunen = Convert.ToSingle(textBox1.Text);

   bolen = Convert.ToSingle(textBox2.Text);

   try

    {

      sonuc = bol(bolunen, bolen);

      textBox3.Text = sonuc.ToString();

    }

   catch (DivideByZeroException Hata)

    {

      MessageBox.Show(Hata.Message);

      textBox2.Focus();

    }

  }

float bol(float s1, float s2)

 {

   if (s2 == 0)

      throw new DivideByZeroException("Sıfıra bölme hatası");

   else 

     return s1 / s2; 

 }



Bu kodda throw anahtar kelimesi ile oluşturduğum DivideByZeroException nesnesinin yalnızca Message özelliğiyle ilgilendim. DivideByZeroException ve diğer Exception sınıflarının ayrıca Source, StackTrace, TargetSite, InnerException gibi özellikleri bulunmaktadır. Örneğin Source özelliği hatanın meydana geldiği Assembly'ın adını tutmaktadır. Hataya neden olan metotlarla ilgileniyorsanız StackTrace özelliğine bakabilirsiniz. Hataya neden olan metotları araştırırken System.Diagnostics'deki StackTrace sınıfından yararlanabilirsiniz. Bu sınıfın nasıl kullanıldığını aşağıda görebilirsiniz.


StackTrace izle = new StackTrace(Hata);

foreach (StackFrame fr in izle.GetFrames())

 {

   listBox1.Items.Add(fr.GetMethod());

 }



Exception Sınıfı Hazırlamak

Yukarıdaki sayfalarda sözü edilen ve hata yakalamaya yönelik bütün sınıflar Exception sınıfından türetilmiş sınıflardır. Hata sınıfınızı kendiniz hazırlamak istiyorsanız hazırlaya­cağınız sınıf Exception veya ApplicationException sınıfının mirasçısı olmalıdır. Programcılar kendi exception sınıflarını özellikle veritabanlarından kaynaklanan hatalar söz konusu olduğunda hazırlama gereğini duymaktadırlar. Bu konuda adım adım bilgi vermek aşağıdaki gibi sınırlı özelliklere sahip bir class hazırladım.


public partial class Hata_sinifim : Exception

 {

   public Hata_sinifim()

    {

     }

 }



Bu sınıfın yapıcı metodu herhangi bir parametre ve satıra sahip değildir. Bu şekilde kendi Exception sınıfınızı hazırladıktan sonra herhangi bir uygulama dahilinde kullanabilirsi­niz. Bu konuda bilgi vermek için forma bir TextBox ve 'Tamam' adını verdiğim bir düğme yerleştirip aşağıda verdiğim kodu hazırladım.


private void Tamam_Click(object sender, EventArgs e)

 {

   if (textBox1.Text == "12345")

     MessageBox.Show("Sisteme giriş yaptınız");

   else

    throw new Hata_sinifim(); 

 }



Kullanıcı çalışma anında formdaki TextBox'a şifresini girip Tamam adını verdiğim düğmeyi tıklayıp bu kodu işletince TextBox'a yazılan bilginin '12345' olup olmadığı araştırılır. Girilen bilgi 12345 değilse throw anahtar kelimesi ile kendi hazırladığım exception sınıfının örneği alınıp fırlatılmaktadır. Kendi hazırla­dığım exception sınıfı, mirasçısı olduğu sınıftan farklı olmadığı için TextBox'a 12345'ten farklı bir bilgi girilirse programın çalışması kırılır ve sistem aşağıda verilen diyalog kutusunu ekrana getirir.



'Tamam' düğmesi için yazılan kodda try-catch bloğuna yer vermiş olsaydım programın çalışması kırılmazdı. Bu amaçla yukarıda verdiğim kodu aşağıdaki gibi düzenledim.


private void Tamam_Click(object sender, EventArgs e)

 {

   try

    {

      if (textBox1.Text == "12345")

         MessageBox.Show("Sisteme giriş yaptınız");

      else

         throw new Hata_sinifim();

    }

  catch (Hata_sinifim Hata)

    {

      MessageBox.Show(Hata.Message);   

    }

 }



TextBox'a 12345'ten farklı bir değer girilip bu kod işletilirse catch bloğundaki satır saye­sinde ekrana aşağıdaki gibi hata mesajı getirilirdi. Çünkü try bloğunda kullanıcının TextBox'a şifre niyetine girmiş olduğu bilginin '12345' olup olmadığını araştırdım.

Kullanıcı TextBox'a 12345 dışında başka bilgi girdiyse throw anahtar kelimesi ile kendi hazırladığım sınıfın örneğini aldım. Başka bir deyişle hatalı bir durumu ihbar edip programın akışının catch bloğuna geçmesini sağladım. catch bloğunda ise throw anahtar kelimesi ile hazırlanan exception nesnenin orijinal mesajını ekrana getirdim.



Kendi hazırladığınız hata sınıfı ile ilgili olarak ekrana kendi hata mesajınızı getirmek istiyorsanız sınıfının yapıcı metodunu aşağıdaki gibi değiştirebilirsiniz. Yaptığım değişiklik sayesinde kendi hazırladığım hata sınıfının yapıcı metodunun string tipte bir parametreye sahip olmasını sağladım.


public Hata_sinifim(string msg) : base(msg) 

 {

 }



Kendi hazırladığınız exception sınıfının yapıcı metodunu bu şekilde değiştirdikten sonra throw anahtar kelimesi ile Exception nesnesini fırlatırken sınıfın yapıcı metoduna aşağı­daki gibi mesaj olarak kullanmak istediğiniz metni parametre olarak vermelisiniz.


throw new Hata_sinifim("Yanlış Şifre Girdiniz");



Yukarıda kendi hazırladığım hata yakalama sınıfına taban sınıf olarak Exception sınıfını seçtim. Ne ki kendi hazırladığınız istisnai durum sınıfları için Exception yerine ApplicationException sınıfını başlangıç noktası olarak seçerniz işiniz kolaylaşcaktır.

Bu konunun üzerinde biraz durabilmenizi sağlamak için şimdi ikinci bir Exception sınıfı hazırlayacağım. Bu amaçla hazırladığım sınıf 4 yapıcı metoda sahip olacak ve ApplicationException sınıfını mirasçısı olacaktır. Kendi hazırladığım Exception sınıfta 4 yapıcı metoda yer vermemin nedeni taban sınıf olarak seçtiğim ApplicationException sınıfın 4 yapıcı metoda sahip olmasıdır.


public partial class Hata_sinifim : ApplicationException

 {

  private string mesajim;

  public string ozellik_mesajim

   {

     get { return mesajim; }

     set { mesajim = value; }

   }

  public Hata_sinifim() : base()

   {

   }

  public Hata_sinifim(string mesaj) : base(mesaj)

   {

   }

  public Hata_sinifim(string mesaj, Exception inner) :base(mesaj, inner)

   {

   }

  public Hata_sinifim(string mesaj, string mesajim) : base(mesaj)

   {

     ozellik_mesajim = mesajim; 

   }

 }



İlk bu şekilde düzenlediğim sınıfın 2. parametresi Exception tipinde olan yapıcı metottan söz edeceğim. Yukarıdaki sayfalarda Exception nesnesinin InnerException özelliğinin adı sayılmıştı. Bu özellik, catch bloğuna yazılan kodlar hataya neden olduğunda işlevsel olmaktadır. Bu konuda bilgi vermek için üzerinde çalıştığım projede bu sınıftan aşağıdaki gibi yararlandım. Tabii bu örneğin pratik bir değerinin olmadığını hemen söylemek gerek. InnerException özelliğinden söz etmek için aşağıdaki gibi 2. sınıf hazırladım.


public class Test_sinifi

 {

  public void Hatayi_uret()

    {

     try

      {

       this.inner_uret(); 

      }

     catch (Exception H) 

      {

       throw new Hata_sinifim("Normal hatamız", H);  

      }

    }

   public void inner_uret()

    {

      throw new Hata_sinifim("inner(önceki) hatamız");

    }

 }



Oldukça basit olan bu sınıfı incelerseniz yapıcı metoda sahip olmayıp Hatayi_uret() adını verdiğim void metotta try-catch bloğu bulunmaktadır. Eğer bu örnekte işlevsellik kaygım olsaydı try-catch bloğuna hataya neden olma ihtimali yüksek satırlar yazardım. Bunu yapmak yerine catch bloğunda hata üretilen veya kendi hazırladığım exception sınıfının örneğinin alındığı inner_uret() metodunu çağırdım. Bu metotta zaten hata üretilmesine rağmen catch bloğunda kendi exception sınıfımın ikinci bir örneğini aldım. Başka bir deyişle bu sınıf sayesinde 2 hatanın arka arkaya meydana gelmesi sağlanmaktadır. Şimdi ise 'Test_sinifi' adını verdiğim bu sınıfın örneğini alacağım. Bu amaçla üzerinde çalıştığım Windows Forms uygulamasında aşağıdaki gibi bir hazırlık yaptım.


private void button1_Click(object sender, EventArgs e)

 {

  Test_sinifi test_nesne = new Test_sinifi();

  try

   {

     test_nesne.Hatayi_uret();

    }

  catch (Exception hata)

   {

     MessageBox.Show(hata.InnerException.Message);

     MessageBox.Show(hata.Message);

   }

}



Hata yakalama ve Exception sınıfları hakkında yazacaklarım bu kadarla sınırlı olmamasına rağmen makale daha da uzamasın diye burada kestim.
  • Etiketler;
Memik Yanık
Hakkında bilgi yakın zamanda eklenecektir.
Yorum Yaz

Yorum yazabilmek için üye girişi yapmanız gerekiyor!

:D :hayret::hayret::hayret: emeğinize sağlık
Çok teşekkürler değerli hocam,emeğine sağlık.

Yukarı Git