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

Bu makalede C# uygulamalarında hata yakama işlemleri ve Exception sınıfları bütün yönleri anlatılmaktadır. Makale uzun olduğu 2 bölüm olarak ele alacağız.

Bu makalede C# uygulamalarında hata yakama işlemleri ve Exception sınıfları bütün yönleri anlatılmaktadır. Makale uzun olduğu 2 bölüm olarak ele alacağız.

Malumunuz olduğu üzere .NET Framework ve C# programlama dili Microsoft firması tarafından hazırlanıp kullanıma sunulalı az olmadı. Bu süreçte yani .NET Framework 1.0 dağıtıldıktan bu yana geçen sürede önce 1.1 sonra 2.0 ve en son olarak 3.0/3.5 sürümleri programcıların istifadesine sunuldu. Benzer gelişme C# programlama dili için de geçerlidir. İçinde bulunduğumuz günlerde .NET Framework 3.5 ile birlikte C# derleyicisinin 3.0 sürümü gelmektedir.

C# kodu içinde çalışma anında meydana gelmesi muhtemel hataları yakalamayla ilgili sınıflarda .NET Framework'ün ilk sürümden bugüne kayda değer bir gelişme olmadığı için C# konulu her kaynakta Exception sınıfları ile ilgili bilgiyi bulmak mümkündür. Ötesi 2002 yılından bu güne kadar hata yakalama sınıflarını anlatan çok sayıda makale programcılık konulu Web sitelerinde yayınlandı.

Örneğin, buradaki yazıda Resul Çavuşoğlu ayrıntılara girmese bile C# kodu dahilinde Exception sınıflarından nasıl yararlanıldığını gayet güzel anlatmaktadır.

İkinci bir kaynak vermek gerekirse; ilk Türkçe C# kitabını yazma ünvanına sahip Ahmet Dizioğlu, Elif İçağasıoğlu ve Levent Çamlıbel'in ortak yazdıkları kitaplarında Exception sınıfları hakkında yeterli bilgiyi bulmak mümkündür. Kısaca özetlemek gerekirse; her kim Exception sınıfları ve hata yakalamak hakkında Türkçe kaynak ararsa, ister kitap edinsin, ister Web sitelerine baksın yeterli kaynağı zorlanmadan bulabilir. Exception sınıfları ile ilgili kaynakların Türkçe olması şartından vazgeçilirse MSDN'i saymasak bile binlerce makale, yüzlerce kitap bulmak mümkündür.

O zaman birileriniz kalkıp bana sormayacak mısınız: Madem bu konuda kaynak bolluğu var, o zaman neden bu konuda 30 sayfa uzunlukta bir makale yazıp yayınladınız?

Tabii, bu soru tek cümle ile cevaplanacak bir soru değildir. 2004 yılında yayınlanan ilk C# kitabımda hata yakalama ve Exception sınıflarının anlatıldığı sayfaların sayısı topu topu 7 idi. Yani yukarıda sözünü ettiğim ve Resul Çavuşoğlu tarafından kaleme alınan makalede, benim kitaptan Exception sınıfları hakkında daha fazla bilgiyi bulmak mümkündü. Çünkü sözünü ettiğim kitabı kaleme alırken kitabın sayfa sayısı fazla artmasın diye bazı konuları ayrıntılı anlatmak yerine kısaca değinmekle yetinmiştim. Sonra 2006 yılının sonunda yayınlanan ve C# 2.0'ı anlattığım kitabımda hata yakalama ve Exception sınıflarına ayırdığım sayfaların sayısını arttırıp 16'ya çıkarmıştım. Çünkü bu kitabımı 2 cilt olarak yayınlamaya karar vermiştim. Her ne kadar Exception sınıflarına C# 2.0 kitabımda 16 sayfa yer ayırmış olsam bile bu konuyu bütün yönleri ile anlattığımı söyleyemem. Derken C# 3.0 kitabımın üzerinde çalışırken Exception sınıflarına ve hata yakalama konusuna torpil geçip normalden daha fazla sayfa ayırdım. Çünkü istedim ki, C# üzerine kitap yazma cüretini gösteren Memik Yanık'ın Exception sınıfları hakkında az çok bilgi sahibi olduğunu insanlar bilsin. Daha doğrusu, Exception sınıfları hakkında bu kadar ayrıntılı metni kaleme alan birisinin sıfıra bölme hatasını anlatırken değişken çalmak üzere başka kaynaklara başvurup başvurmayacağı konusunda işin meraklıları fikir sahibi olsunlar istedim. Bu metin yakında yayınlanacak C# 3.0 kitabımdaki ilgili bölümün küçük değişiklikler, eklemeler ve çıkarmalar yapılmış halidir.

Yazarlar Neden Yazarlar?

Bence bu sorunun cevabı basittir ve kısadır: Yazarlar bildiklerinin, düşündüklerinin, hissettiklerinin bilinmesini isterler. Yani bir arkadaş bilgisayarın başına oturup LINQ konulu bir makale yazıyorsa, asıl demek istediği bence şudur:

- "Sayın okurlar, bakın ben bu konuda günlerce araştırma yaptım, araştırmalarım sırasında öğrendiklerimi test ettim, sindirdim. Sizi bu konuda haberdar etmek istiyorum".

Zaten bu kaygı ile kaleme alınan metin okunmaya değer olur. Tabii ki şartların zorlaması sonucu kaleme alınan tez, rapor vs. gibi metinler genellikle yukarıda işaret ettiğim güdü ile yazılmaz.

Tabii ki, bu uzun makalede bozuk cümleler, yazım hataları, hatta teknik hatalar bile olabilir. Bu durumda okurun birisi beni hatadan haberdar ettiği zaman kendisine teşekkür edip hatamı düzeltme yoluna giderim.

Eklemek istediğim 2. bir konu var: Microsoft ve diğer programlama dili üreten firmalar geliştirdikleri programlama dili ile ilgili bütün ayrıntıları açıklarlar. Yoksa insanların söz konusu programlama dilini öğrenip kullanmaları mümkün olmazdı. Bundan çıkardığım sonuç şudur:

Öğretim üyeleri ve yazarlar olsa olsa programlama dilini kendi cümleleri ile anlatırlar; yeni bir şeyler söyleme ihtimali yoktur. Yazarların ve hocaların olsa olsa şöyle bir iddiası olabilir: Bu konuda bilgi sahibi olmayan birisine ben Exception sınıfları ve hata yakalamayı daha iyi anlatırım, Exception sınıflarını anlatırken vereceğim örnek kodlar daha anlaşılırdır vs. Yani elin gavuru bir programlama dili hazırlamış. Ötesi programlama dilinin bütün özelliklerini, metotlarını, sınıflarını vs. örnekleri ile açıklayıp anlatmış. Biz yazarlar ve hocalar ne yapıyoruz? Kendimizce bazı konuların önemli olduğuna kanaat getirip o konular hakkında fazladan birkaç cümle yazıyoruz. Bunu yaparken bilim yapmıyoruz, Amerika'yı yineden keşfetmiyoruz. Nasıl ki Türk Milli Takımının maçı hakkında ertesi gün bütün gazetelerin verdikleri kadrolar aynı ise, golleri atan futbolcular aynı ise, bütün hocalar, bütün yazarlar C# programlama dilinden söz ederken int tipindeki değişkenlerin nasıl tanımlandığını da benzer cümleler ile anlatırlar.

İddiam olsa olsa şu olabilir: Exception sınıfları bu şekilde anlatılırsa daha kolay anlaşılır. Başka bir yazar veya hocanın yoğurt yiyişi farklı olacağı için bu sınıfları farklı cümlelerle anlatır. Madem anlatılan konu aynıdır; o halde o cümleyi kim kaleme alırsa alsın bütün okurların aynı şeyi anlaması gerekir. Eğer bazı okurlar anlatılanlardan farklı sonuçlar çıkarıyorlarsa, kabahat yazarındır ve yazar cümlelerinin çerçevesini iyi ayarlayamamıştır.

Projeleri Çalıştırmak

Bu metni hazırlarken Visual Studio'dan yararlandım. Sizler ücretsiz ve C# için hazırlanmış Express sürümden yararlanabilirsiniz. Tabii bilgisayarınıza .NET Framework'ü kurup(ki .NET Framework Windows Vista ile birlikte geliyor) Not Defteri ile kodunuzu yazıp konsolda yani DOS penceresinde derleyebilirsiniz. Bu şartlarda çalışma anında meydana gelecek hataları yakalamada herhangi bir sorun yaşamazsınız ama kodunuzdaki hataları ayıklamanız zahmetli olur. Çünkü Visual Studio ve Express Editon'la birlikte entegre çalışan bir hata ayıklayıcı(Debugger) verilmektedir. İşin özüne inebilmek için işe Visual Studio ile hazırlanan projelerin çalıştırılması ile başlayacağım.

Bu amaçla Visual Studio ile gelen Windows Forms Application şablonundan yararlanıp "Proje1" adında C# projesi hazırladım. Bu projeyi "C:" sürücüsünde "Proje1" klasörüne kaydettim. Projeyi kaydettiğim klasörü özellikle işaret ettim. Çünkü proje çalıştırıldığında geri planda Visual Studio tarafından yapılan işlemlerin üzerinde biraz durmak gerekecek. Şimdi hazırladığım projede herhangi bir değişiklik yapmadan, başlangıç formuna kontrol yerleştirmeden ve proje için başka bir Class hazırlamadan çalıştıracağım. Projeleri çalıştırmak için genellikle Debug menüsündeki Start Debugging komutu kullanılmaktadır. Genellikle dedim; çünkü projeleri çalıştırmanın başka yöntemleri de var.

Her ne kadar konunun başlığı Projeleri Çalıştırmak olsa bile bu amaçla kullanacağım komutun adı Start Debugging. Yani yazdığım projeyi Debug etmek, test etmek ve mevcut hatalardan ayıklamak istiyorum. Başlangıçta Visual Studio penceresinin başlığı Proje1-Microsoft Visual Studio iken proje çalıştırıldığında Visual Studio penceresinin başlığı Proje1[Running]-Microsoft Visual Studio olmaktadır. Start menüsünden bu komutu vermek demek, projeyi entegre Debugger'ın kontrolünde test etmek demektir.



Visual Studio ile hazırlanan C# projeleri Debug menüsünden komut verilerek çalıştırıldıkları zaman Visual Studio tarafından otomatik olarak EXE dosya hazırlanmaktadır. Proje çalıştırıldığında hazırlanan EXE dosyanın yerini aşağıda görebilirsiniz. Bu EXE dosya ancak proje hazırlanırken tercih edilen .NET Framework sürümünün(2.0, 3.0 veya 3.5) kurulu olduğu bilgisayarda çalışabilir. Aşağıda ekran görüntüsü verilen dosya listesindeki "pbd" uzantılı dosyaya dikkatinizi çekmek istiyorum. Kodun debug edilmesi yani hatalardan ayıklanması ile ilgili bilgiler bu dosyaya yazılmaktadır. Tabii bu makalede hata ayıklama işlemleri üzerinde durmayacağımız için pbd uzantılı dosyanın işlevinden ve nasıl kullanıldığında söz edilmeyecektir.



Yukarıda söylendiği gibi Visual Studio ile hazırladığınız C# projelerini çalıştırmak için Debug menüsünden Start Debugging komutunu verebilir veya direk F5 tuşuna basabilirsiniz. Aslında Debug menüsünden bu komutu vermekle Visual Studio'ya bir bakıma "hazırladığım projeyi entegre hata ayıklayıcısının nezaretinde test etmek istiyorum ve varsa hataları ayıklamak istiyorum" demiş oluyorsunuz. En başında belirtmek gerekir ki Start Debugging komutu verildiği zaman proje için otomatik olarak hazırlanan EXE dosya başkalarının kullanıp yararlanacağı kopya değildir. Elbette Start Debugging komutu sayesinde hazırlanıp projeye ait klasörün içinde yer alan "BinDebug" klasörüne yerleştirilen EXE dosyayı alıp başka bilgisayarda çalıştırmak mümkündür. Ancak debug modunda iken hazırlanan EXE dosyanın dağıtılması önerilmiyor.

Şimdi gelelim şu Debug moduna. Programcılar genelde uygulamalarını geliştirmeyi ve test etmeyi Debug modunda yaparlar. Ne zaman ki uygulama tamamlanıp testlerden geçer o zaman Release sürümü hazırlayıp kullanıcılara öyle verirler. Yukarıdaki sayfalarda işaret edildiği gibi Visual Studio ile yeni C# projesi hazırlanıp kaydedildiği zaman projeye ait klasörün içinde "Bin" ve "Obj" adında 2 klasör hazırlanmaktadır. Derleme sırasında hazırlanan geçici dosyalar "Obj" klasörüne konulmaktadır. Obj klasörünün altında "Debug" ve "Release" adında 2 klasör hazırlanmaktadır. Aynı şekilde "Bin" klasöründe "Debug" ve Release" adında 2 klasör hazırlanmaktadır. Uygulamanın Debug sürümü Bin klasörünün içinde bulunan BinDebug klasörüne konulurken Release sürümü BinRelease klasörüne yerleştirilmektedir.

Debug modunda iken proje Debug menüsündeki Start Debugging komutu ile çalıştırıldığında hem "Obj" klasörünün altındaki Debug klasörüne hem de "Bin" klasörünün altındaki "Debug" klasöründe bazı dosyalar oluşmaktadır. Üzerinde çalıştığınız projeyi Ctrl+F5 tuşları ile çalıştırırsanız projeyi Debugger'dan bağımsız çalıştırmış olursunuz.

Debug modundan Release moduna geçmek istiyorsanız Visual Studio'nun Debug menüsünden komut verip Cunfiguration Manager diyalog kutusunu ekrana getirmelisiniz. Bu komut Debug menüsünde yer almıyorsa Tools menüsündeki Options komutu ile ekrana getirilen diyalog kutusunda ayarlama yapmalısınız.



Bu ekran görüntüsünü aldığım sırada üzerinde çalıştığım projenin dahil olduğu Solution bir tek projeye sahipti. Bu sırada Solution'da birden fazla proje olsaydı o projeler de listelenirdi. Bu diyalog kutusundaki Active Solution configuration ve Active solution platform liste kutularında yapılan seçimlerden Solution'daki bütün projeler etkilenmektedir. "Active Solution configuration" liste kutusunu açıp Release'i seçerseniz Debug modundan Release moduna geçmiş olursunuz. Bu andan itibaren Debug menüsünden Start Debugging komutunu verip projeyi çalıştırırsanız EXE kodun Release sürümü hazırlanır ve "BinRelease" klasörüne yerleştirilir.



Hataları Yakalamak

Visual Studio ile C# uygulaması geliştirilirken konu hatalar olduğunda akla hemen iki kavram gelmektedir: Hata Ayıklamak ve Hata Yakalamak. Visual Studio ile birlikte gelen entegre hata ayıklayıcı(Debugger) sayesinde ilk bakışta görülmeyen hataları ayıklayabilmektesiniz. Bu bölümün konusu çalışma anında gelişen şartlara bağlı olarak meydana gelen hataları yakalamaktır.

Bir önceki bölümde bütün ayrıntıları ile olmasa bile hata ayıklama ve entegre debugger hakkında bilgi verildi. Hata ayıklama ve Debug işlemi hakkında anlatılanlar daha çok programcının işini kolaylaştırmaya yönelik işlemlerdi. Programcılıkta hata denildiği zaman asıl çalışma zamanında kullanıcının başrolde olduğu hatalar akla gelmektedir. Örneğin kullanıcıdan doğum tarihinin günü istenir ve gün bilgisi olarak kullanıcı 31'den büyük bir değeri girerse programcı herhangi bir tedbir almadıysa hata meydana gelir ve projenin çalışması kırılır. İşte bundan sonraki sayfalarda çalışma anında karşılaşılması muhtemel olan hataları nasıl yakalayabileceğinizden adım adım söz edilecektir.

C# projelerinde hata yakalama try-catch-finally blokları ile yapılmaktadır. Hata yakalama işlemlerinin nasıl yapıldığını örnek bir olay üzerinde anlatmak istiyo­rum. Kullanıcıdan bir işlem için tarih istediğinizi varsayalım. TextBox'a tarih olarak değerlendirilemeyecek bilgi girilirse programın çalışması kırılabilir. Aşağıda verilen metot işletil­di­ğinde TextBox'ın içeriği dönüştürülüp DateTime tipindeki değişkene aktarılır.


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime Tarih;

   Tarih = Convert.ToDateTime(textBox1.Text);    

 }



Yerli yabancı yazarlar hata yakalamanın nasıl yapıldığını anlatmak istediklerinde genellikle kullanıcıdan DateTime tipinde bilgi isterler. Benim için tehlike asıl burada başlıyor: Şimdi birisi kalkıp "hata yakalama işlemini anlatırken kullanıcıdan tarih istemeyi senden önce akıl ettim, bu fikrin patenti bende derse" ne yaparım, kendimi nasıl savunurum? Tabii buna bir de değişken adı benzerliği eklenirse yandım demektir. Şimdi izninizle yukarıda verdiğim kodu biraz değiştireceğim ve "Tarih" yerine farklı ada sahip değişken tanımlayabildiğimi kanıtlamaya çalışacağım. Tabii burada benim için ikinci bir tehlike daha var: Ya benden önce yazarın birisi kitap veya makale yazıp DateTime tipindeki değişkene aktaracak bilgiyi kullanıcıdan TextBox aracılığı ile istemişse? Yani anlayacağınız programcılık üzerine yazmak zor iş.


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime History;

   History = Convert.ToDateTime(textBox1.Text);    

 }



Kullanıcı TextBox'a tarih veya zaman olarak değerlendirilebilinecek bilgi girdiği sürece bu kod hatasızca çalışır. Ne zamanki kullanıcı DateTime tipine dönüştürülemeyecek bilgiyi TextBox'a girip başka bir nesnenin üzerine giderse bu kod hata verir ve programın çalışması kırılır. Burada yapılması gereken şudur: Hataya neden olma ihtimali yüksek olan bu satırı try bloğuna almak ve meydana gelebilecek hataları catch bloğunda yakalamaktır. Bu amaçla yukarıda verdiğim kodu aşağıdaki gibi düzenledim.


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime History;

   try

    {

      History = Convert.ToDateTime(textBox1.Text);

    }

   catch

    {

      MessageBox.Show("Girdiğiniz tarih veya saat yanlış");

      textBox1.Focus();  

    }

  }



Bu kodda hataya neden olabilme ihtimali olan satırı try bloğuna aldım. Ardından catch bloğunda tahmin ettiğim hata meydana geldiğinde yapılacak işlemler için hazırlık yaptım. catch bloğunda kullandığım Focus() metodu sayesinde kontrol tekrar formdaki ilk TextBox'a geçer. 2. veya 3. denemede kullanıcı TextBox'a uygun bilgi girerse hata meydana gelmez ve dolayısıyla catch bloğuna yazılan satırlar işletilmez. catch-try bloklarının çalışma şeklindeki ayrıntıları ortaya çıkarmak için Convert sınıfının ToDateTime() metodu ile string bilginin DateTime tipine çevril­diği satırı ayrı bir metot olarak düzenledim("Tarih" adına sahip olmayan değişken tanımlayabilme becerisine sahip olduğumu bu şekilde kanıtladıktan sonra yine Türkçe değişken adına dönelim).


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime Tarih;

   try

    {

     Tarih = tarih_yap(textBox1.Text);

    }

   catch

    {

      MessageBox.Show("Girdiğiniz tarih veya saat yanlış");

      textBox1.Focus();  

    }

  }

 public DateTime tarih_yap(string str)

  {

    return Convert.ToDateTime(str);

  }



Her ne kadar Convert sınıfının ToDateTime() metodu ile string bilgiyi DateTime tipinde bilgiye dönüştüren satır ayrı bir metodun içinde yer alsa bile "tarih_yap()" adını verdiğim bu metot try bloğunun içinden çağrılıp işletildiği için bu metotta meydana gelecek hatalar yine yakalanır. Başka bir deyişle try bloğunun içinden çağrılan tarih_yap() metodunda herhangi bir hata meydana gelmesi halinde programın işletimi yine catch bloğuna geçer.

Bundan şu sonuç çıkarılabilir: C# uygulamalarının başlangıç noktası Main() metodu içinde try-catch bloğu hazırlanırsa, başka bir deyişle uygulamayı başlatan Run() metodu try bloğuna yazılırsa uygulamanın neresinde hata meydana gelirse gelsin bu hata yakalanır. Eklemek gerekirse, C# uygulamaları Main() metodundan itibaren çalışmaya başlar ve Main() metodunun sonuna gelindiğinde ise uygulamanın çalışması sona ermiş olur.


static void Main()

 {

   try

    {

      Application.Run(new Form1());

    }

   catch 

    {

     MessageBox.Show("Herhangi bir yerde hata meydana geldi");  

    }

  }



try-catch-finally blokları ile ilgili olarak hemen söylenmesi gereken birkaç kural daha var. Örneğin try bloğundan hemen sonra ya catch ya da finally bloğu gelmelidir. Bu nedenle aşağıdaki gibi düzenlenen try-catch bloğu derleme sırasında hataya neden olur. Çünkü bu kodda try ile catch blokları arasında bir satır bulunmaktadır.


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime Tarih;

   try

    {

     Tarih = Convert.ToDateTime(textBox1.Text);

     }

   this.Text = "Hata yakalama blokları"; 

   catch

    {

     MessageBox.Show("Girdiğiniz tarih veya saat yanlış");

     textBox1.Focus();  

    }

 }



try-catch-finally blokları ancak bir metodun içinde yer alabilirler. Çünkü C# derleyicisi ancak metotlar şeklinde düzenlenmiş kod parçalarını işletebilmektedir. Bu nedenle try bloğu içinde metot kodlanmak istenirse derleme sırasında hata meydana gelir. Hata yakalama konusuna bu şekilde giriş yaptıktan sonra şimdi biraz işin geri planı üzerinde duralım. Bildiğiniz gibi programcılar genellikle uygulamalarını Debug modunda hazırlayıp entegre hata ayıklayıcısının desteği ile hatalardan ayıklarlar. Ayrıca çalışma anında karşılaşılan kullanıcı kaynaklı hatalarından korunmak veya içinde bulunulan şartlardan dolayı meydana gelmesi muhtemel hataları yakalayabilmek için try-catch blokları hazırlarlar. Daha sonra uygulamalarının Release sürümlerini hazırlayıp kullanıcıların istifadesine sunarlar.

Bu döngüyü gözetip yukarıda verdiğim koddaki try-catch deyimlerini sildim. Başka bir deyişle olası hataları yakalamak üzere yaptığım hazırlıkları iptal ettim. Devamında Build menüsünden Build komutunu verip üzerinde çalıştığım projeyi derledim. Daha önceki konulardan bildiğiniz gibi Build menüsünden komut verilip üzerinde çalışılan proje derlendiğinde Debug modunda iken hazırlanan EXE dosya "bindebug", Release modunda iken derleme yapıldığında ise EXE dosya "binrelease" klasörüne konulmaktadır. Derleme işlemini yaparken Debug modunda olduğum için derleme sonucu hazırlanan EXE dosya aşağıda işaret edildiği üzere "bindebug" klasörüne yerleştirildi.



Bu klasördeki EXE dosya çift tıklanıp proje çalıştırılıp TextBox'a girilen bilginin tarihsel bilgiye dönüştürülmesi sırasında hata meydana gelmesi sağlanırsa ekrana aşağıda verdiğim diyalog kutusu gelir. Bu diyalog kutusundaki Continue düğmesini tıklayıp hatanın atlanılıp göz ardı edilmesini sağlayabilir veya Quit düğmesini tıklayıp uygulamanın çalışmasını sona erdirebilirsiniz. Meydana gelen hata hakkında ayrıntılı bilgi edinilmek istendiğinde ise Details düğmesi tıklanır.



Tahmin edeceğiniz gibi bu proje Debug menüsündeki Start Debugging komutu ile çalıştırılsaydı bu hata ile karşılaşıldığında projenin çalışması kırılırdı ve hataya neden olan satır işaret edilirdi. Çünkü Debugger'ın başlangıç ayarlarında böyle bir hata ile karşılaşıldığı zaman uygulamanın çalışması sona erdirilmektedir. Yukarıda ise uygulamayı entegre debugger'dan bağımsız çalıştırdığım için uygulamanın çalışması kırılmadı. Eğer yukarıda yaptığım gibi çalışma anında meydana gelmesi muhtemel bu hatayı yakalamak için try-catch bloğu hazırlasaydım projeyi ister debug modunda, ister Visual Studio kurulu olmadığı başka bir bilgisayarda çalıştırmış olsaydım değişen bir şey olmazdı.

Şimdi hata yakalama konusunda 2. bir örnek vereceğim. Hazırlayacağım örnekte 5 elemanlı bir dizi değişken tanımlayıp for döngüsü ile dizi değişkenin elemanlarına bilgi aktaraca­ğım. Dizi değişkene aktaracağım bilgileri forma yerleştirmiş olduğum ListBox'tan alacağım. Aşağıda verilen 4 satırlık kodda tanımladığım 3 değişken("Sayi", "Takimlar" ve "i") C# programcıları ve yazarlar tarafından çok sık kullanılan değişken adlarıdır. "i", "sayi" ve "takimlar" adlı değişkenleri başka kaynaklardan yararlanmadan tanımlayabildiğime bu konuda araştırma yapanları belki inandırabilirim. "Sayi" yerine "number", "i" yerine "I" ve "takimlar" yerine "ekipler" adında değişkenler tanımlayabilirdim.





ListBox, 5 veya daha az elemana sahip iken bu kod sorunsuz olarak çalışır. Ancak ListBox'ın eleman sayısı 5'ten fazla iken bu kod işletildiğinde programın çalışması kırılır ve aşağıdaki gibi bir hata mesajı alınır.




Tahmin edeceğiniz gibi uygulamanın kırılmasını sağlayan entegre hata ayıklayıcıdır. Eğer bu projeyi derleyip EXE dosyayı Visual Studio'dan yani Entegre hata ayıklayıcıdan bağımsız çalıştırsaydım uygulamanın çalışması kırılmayıp aşağıdaki gibi bir mesaj alınırdı.



Bu hatanın meydana gelme nedeni dizi değişkenin olmayan 6. elemanına bilgi aktarıl­mak istenmesi­dir. Yapılan tanımlamadan dolayı dizi değişken 5 elemana sahip olduğu için hata meydana geldi. Bu gibi hataların önüne geçip programın çalışmasının kırılma­sını önlemek için aşağıdaki gibi try-catch blokları hazırlanabilir.





Bu kodda hata meydana geleceğini öngördüğüm satırları try bloğuna aldım. Bu satırlarda hata meydana gelmezse kod sorunsuz çalışır ve programın işletimi catch bloğundan sonraki ilk satıra geçer. Hata meydana gelirse hatanın meydana geldiği noktada işleme ara verilir ve catch bloğundaki satırlara geçilir.

Bu örnekte catch bloğunda kullanıcıya mesaj vermekle yetindim. Bu şekilde düzenlenen kodun prog­ramın çalışma­sını kırmadığını göstermek için forma 2. bir ListBox yerleşÂ­tirdim ve AddRange() metodu ile dizi değişkenin elemanlarını 2. ListBox'a aktardım



Şimdi yukarıda hazırladığım catch bloğunda değişiklik yapacağım. Hazırlamış olduğum örnekte try bloğundaki satırlar nasıl bir hataya neden olurlarsa olsunlar, başka bir anlatımla nasıl bir hata meydana gelirse gelsin catch bloğu işletilir. Bu catch bloğundaki satırların yalnızca dizi değişkenlerle ilgili olarak indis sorunu yaşandığında işletilmesini isteseydim aşağıdaki gibi düzenleme yapardım.

try bloğundaki satırlarda çalışma anında dizi değişkenlerle ilgili bir hata meydana geldiğinde IndexOfRangeException sınıfının örneği hazırlanıp catch bloğuna gönderilir(aşağıda verdiğim kodda "Hata" yerine "Hataspor" adında değişken tanımlayıp kullandım. Birkaç satırdan meydana gelen bu kodda bilseydim ki Orhan babaya ayıp olmaz "hata" yerine "hatalarimla_sev_beni" adında bir değişken tanımlayıp kullanırdım.





Bu örnekte sistem tarafından fırlatılan istisnanın veya hazırlanıp catch bloğuna gönderi­len IndexOutOfRangeException tipindeki nesnenin Message özelliğinin içeriğini ekrana yazdım. Exception sınıfından türetilen IndexOutOfRangeException sınıfının Message özelliğinden başka Source, HelpLink, StackTrace, TargetSize ve InnerException gibi özellikleri de bulunmaktadır.

Bu kodu tekrar yorumlamak gerekirse; try ve catch blokları birlikte çalışmakta ve try bloğundaki herhangi bir satırda hata meydana geldiğinde meydana gelen hataya göre hemen bir Exception nesnesi hazırlanıp programın işletimi catch bloğuna geçmektedir.

Yukarıda verdiğim kodda catch bloğuna gönderilen exception nesnesinin IndexOutOfRangeException tipinde olup olmadığını araştırdım. Bu sırada catch bloğuna gönderilen Exception nesnesi IndexOutOfRangeException sınıfının örneği ise catch bloğuna yazılan satırlar işletilir.

try bloğunda meydana gelen hatadan dolayı catch bloğuna gönderilen bütün nesneler System'de bulunan Exception sınıfından türetilen sınıfların örnekleridir. Bu kodda dizi değişkenin olmayan bir elemanı üzerinde işlem yapılmak istendiği için catch bloğuna IndexOutOfRangeException tipinde bir nesne gönderilir. Konu üzerinde düşünmenizi sağlamak için bu makalenin ilk sayfalarında verdi­ğim örneği aşağıdaki gibi düzenledim.


private void textBox1_Leave(object sender, EventArgs e)

 {

   DateTime History;

   try

    {

     History = Convert.ToDateTime(textBox1.Text);

    }

   catch(IndexOutOfRangeException Hata)

    {

      MessageBox.Show("Girdiğiniz tarih veya saat yanlış");

      textBox1.Focus();

    }

 }



Bu şartlarda TextBox'a DateTime tipine dönüştürülemeyecek bilgi girilip TextBox'tan ayrılıp Leave olayı meydana getirildiğinde try bloğundaki satırlar hataya neden olur ve programın işletimi catch bloğunda geçer. Ne ki catch bloğunda dönüştürme işlemi sonucu meydana gelen hata yakalanamaz. Çünkü catch deyimine ait parantezin içinde IndexOutOfRangeException tipinde değişken tanımlandığı için catch bloğundaki satırlar işletilmeyip meydana gelen hatadan dolayı programın çalışması kırılır.

Şimdi ise hata yakalamanın nasıl yapıldığı anlatılırken ilk akla gelen sıfıra bölme hata­sını yakalayan bir örnek vereceğim. Bu amaçla forma 3 TextBox ve 1 düğme yerleştirdim. İlk 2 TextBox'a bilgi girilip "Hesapla" adını verdiğim düğme tıklandığında ilk 2 TextBox'ın içeriklerini Byte tipindeki değişkene aktarıp 1. sayıyı ikinciye bölüp çıkan sonucu 3. TextBox'a yazacağım. Bu işlemi yapacak kodu aşağıda verdim.


private void Hesapla_Click(object sender, EventArgs e)

 {

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

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

   float Sayi3 = Sayi1 / Sayi2;

   textBox3.Text = Sayi3.ToString();   

 }



İzninizle tam burada biraz durup bir şeyler eklemek istiyorum. Bu 4 satırlık kod ile ilgili olarak önce amacımın ne olduğunu ortaya koyacağım. Amacım net: Sıfıra bölme hatasını anlatacağım. Eğer ilkokul öğrencilerine sıfıra bölme hatasını anlatıyor olsaydım elime bir hesap makinesi alır ve herhangi bir sayıyı sıfıra bölerdim. Hesap makinesi sıfıra bölme işleminin sonucu olarak LCD ekranına yazdığı Error'u öğrencilere gösterip onları kolayca ikna ederdim. Madem konumuz programcılık ve kullanıcıların yapması muhtemel sıfıra bölme hatasını yakalayacağız; birbirine böleceğimiz 2 sayıyı kullanıcıdan isteriz. Ayrıca kullanıcıya mümkünse ikinci sayının yani bölenin sıfır olmasını rica ederiz. Amacımız sıfıra bölme hatasını yakalamak olduğuna göre hata yapılmadan hatayı yakalama imkanımız olamayacağına göre kullanıcıdan 2 sayı isteyeceğiz. Asıl sorun ise kullanıcıdan sayıları isterken yaratıcı olabilmek veya kimsenin aklına gelmeyen bir tekniği kullanabilmektir.

Kullanıcıdan sayı isterken "bu sayıları şu TextBox'lara gir" dersek başkalarının fikrini çalmış olabiliriz. Ne yazık ki yıllardır bilgisayar ve programcılık üzerine yazdığı binlerce sayfada milyonlarca cümleyi kuran Memik YANIK bundan 4 yıl kadar C# üzerine yazarken boş bulunup birbirine böleceği 2 sayıyı kullanıcıdan isterken TextBox'lara girmesini istemişti. Tabii bununla kalmayıp kullanıcının TextBox'lara girdiği bilgileri dönüştürüp Sayi1, Sayi2 adında 2 değişkene aktarmıştı. Memik Yanık bu 2 değişkeni yıllar önce yazdığı kitaplarında kullanmış olsa bile bu değişkenleri namı hesabına kaydetmeyi, register etmeyi akıl etmemiş.

Başka makale ve kitap yazarlarının telif haklarına tecavüz etmemek için bu 4 satırlık kodu aşağıdaki gibi düzenledim. Bu kodda TextBox'lara girilecek sayılar byte tipindeki değişkenlere aktarılmaktadır. Formdaki ilk 2 TextBox'a 255'ten küçük bir sayı girilirse bu kod hatasızca çalışır. Ancak kullanıcı 2. TextBox'a 0 yazarsa sıfıra bölme hatası meydana gelir ve programın çalışması kırılır.


private void Hesapla_Click(object sender, EventArgs e)

 {

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

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

   float Number3 = Number1 / Number2;

   textBox3.Text = Number3.ToString();   

 }



Aynı şekilde TextBox'lara 255'ten büyük bir değer yazılırsa taşma(değişkenler byte tipinde olduğu için) meydana gelir ve programın çalışması kırılır. Hata meydana geldiği zaman programın çalışmasının kırılmasını engellemek için try-catch bloğu hazırladım. Değişkenlere Sayi1, Sayi2 ve Sayi3 gibi adlar vermenin mecburi olmadığını böylece kanıtladıktan sonra tekrar Türkçe değişken adlarına dönelim.


private void Hesapla_Click(object sender, EventArgs e)

 {

   byte Sayi1, Sayi2;

   float Sayi3;

   try

    {

      Sayi1 = Convert.ToByte(textBox1.Text);

      Sayi2 = Convert.ToByte(textBox2.Text);

      Sayi3 = Sayi1 / Sayi2;

      textBox3.Text = Sayi3.ToString();

    }

   catch

    {

      MessageBox.Show("Hata meydana geldi");

      textBox1.Focus();  

    }

 }



Ek açıklama yapmak gerekirse bu koddaki try bloğundaki satırlar işletilirken ister taşma hatası ister sıfıra bölme hatası meydana gelsin her şartta kullanıcıya catch bloğundaki mesaj verilip tekrar ilk TextBox'ın üzerine gidilir. Konu üzerinde düşünmenizi sağlamak için bu kodun catch bloğunu aşağıdaki gibi düzenledim. C# programlama dilinin ne "Error" ne de "error" adında bir anahtar kelimesi olmadığı için bu kodda yakalanacak Exception nesnesinin aktarılacağı değişkenin adını "Hata" olarak seçmek yerine "error" adını kullanmanız önerilir. Elin gavurları gelip bu değişken bana aittir demeyeceğine göre sorun yaşamazsınız.


catch(Exception Hata)

 {

   object Tip = Hata.GetType();

   MessageBox.Show("Hata meydana geldin Meydana gelen Hata:"+ 

Tip.ToString());

   textBox1.Focus();  

 }



Burada yapılan şudur: catch anahtar kelimesine ait parantezlerin içinde Exception tipinde bir değişken tanımladım. catch deyimi, try bloğundan gönderilen nesnenin tipinin parantezlerin içinde tanımlanan değişkenle aynı tipte olup olmadığına bakar. Gönderilen Exception nesnesi System.Exception tipinde ise catch bloğundaki satırlar işletilir. Hata nedeniyle try bloğundan catch bloğunda gönderilen exception nesnesi­nin tipi ister IndexOutOfRangeException olsun ister OverflowException her şartta catch bloğundaki satırlar işletilir. Çünkü IndexOutOfRangeException ve OverflowException sınıfları System.Exception sınıfından türetilmiş sınıflardır.

Bu koda önce object tipinde bir değişken tanımlayıp catch bloğuna gönderilen nesnenin tipini GetType() metodu ile öğrendim. Catch bloğundaki satırların işletilmesinin nedeni sıfıra bölme iken aşağıdaki gibi bir mesaj alınır



Gördüğünüz gibi bu sırada meydana gelen hata sıfıra bölme işlemi kaynaklı olduğu için catch bloğuna DivideByZeroException sınıfının örneği gönderilmiş. TextBox'lardan birisine Convert sınıfının ToByte() metodu ile byte tipine dönüştürülemeyecek bilgi girip ondan sonra yukarıda verdiğim kodu işletseydim farklı bir hata mesajı alırdım.
  • Etiketler;
Yorum Yaz

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

• 12 yıl önce
twk ederim...Allah razi olsun
• 11 yıl önce
Hocam teşekürler,altın değerinde bir bilgi aldım sizden.
• 9 yıl önce
çok çok teşekkürler. Süper ötesi olmuş.

Yukarı Git