Memik Yanık - Kişisel Web Sitesi Ana Sayfa | Hakkımda | Kitap Önerileri | Fotoğraflarım | RSS RSS | İletişim

Programcılar İçin Yazarlık Dersleri - 3


Hata Yakalama ve Exception Sınıfları’nı Nasıl Anlattım? 

Bu makalede programcılık üzerine yazanlara, öğrencilere programcılık öğretenlere ve kendi başına programcılık öğrenenlere hitap etmeye çalışılmaktadır. Bu bölüm okunduktan sonra programcılık üzerine yazılan bazı metinlerin neden zor okunduğunu, neden anlaşılmaz olduğunu fark edeceksiniz. Bu derste hem genel konular üzerine konuşacağız hem de C# 3.0 kitabımdan aldığım Hata Yakalamak ve Exception Sınıfları makalesini Yazarlık Dersleri bağlamında ele alacağız, neyi neden nasıl anlattığımın hesabını vereceğim. Başka bir deyişle C# 3.0 kitabım için hazırlayıp sonra kişisel web sitemde konunun meraklılarıyla paylaştığım 30 sayfalık metni bu bölümde ele alacağım. Programcılık üzerine yazanlar bu metni dikkatlice okuduktan sonra dönüp daha önce yazdıklarına burun kıvıracaklarına, ilk fırsatta yazma ve anlatma stillerinde köklü değişikliklere gideceklerine inanabilirsiniz. 

Memik Yanık’ın İlk C# Kitabı 

Bu derste asıl konumuz Hata Yakalamak ve Exception Sınıfları bağlamında programcılık kitaplarını nasıl kaleme aldığımı anlatmak olduğundan konudan uzaklaşmamak için kendi C# kitaplarıma dönüyorum. 2004 yılında yayınlanan C# kitabım içerik bakımından ortalama bir kitaptı. Yani C# ve .NET programcılığına henüz başlayanları belli bir düzeye çıkarmayı hedefliyordu. Bu hedefin içinde veritabanı uygulamalarını geliştirebilmek, API fonksiyonlarını kullanabilmek, basit web servisi yazabilmek bile vardı. Başka bir deyişle 900 sayfalık kitapta çok sayıda konuya yer yermeye çalışmıştım. Böyle olunca kitaptaki bazı konular ayrıntı içermiyordu. Örneğin yukarıda sözünü ettiğim makalede anlattığım konu, yani hata yakalama konusu topu topu 7 sayfaydı. Tabii ki 900 sayfalık bu kitap bazı okurlar için gerek duymadığı konuları içerirken bazı okurların asıl gerek duydukları konular yoktu veya yeteri ayrıntıda anlatılmamıştı. 

Sözünü ettiğim kitapta 7 sayfa boyunca anlattığım Hata Yakalamak adlı bölümde sıfıra bölme hatasından söz etmiştim. Çünkü sıfıra bölme programcılık derslerinin mutlaka anlatılan klasik hatalarından birisidir. Yoksa serileştirme yapılırken karşılaşılan hatalardan söz edecek değildim. Çünkü serileştirme işlemi iyice anlatılmadan serileştirmeyle ilgili hataları veya Application Domain’e assembly yüklerken karşılaşılan hataları yakalayıp anlatmak anlamsız olurdu. Aslında bazılarınız “madem her kitapta sıfıra bölme hatasından söz ediliyor siz de başka hatalardan söz edin” diyebilirler. 

Bu uyarıyı yapan arkadaşlara cevabım hazır: Yukarıda sözünü ettiğim ilk C# kitabım yayınlandıktan çok sonra birileri çıkıp “ey memik yanık sen sıfıra bölme hatasını biliyor olamazsın” demesin mi? Yani 1994 yılından bu yana sayısız programcılık kitabına imza atan memik yanık sıfıra bölme hatasından bi haberdir diye düşünenler varken sizler kalkıp bu konu gereksizdir, C#’la ilgilenen herkes zaten sıfıra bölme hatasını biliyordur diyorsunuz. Otoritelerin(!) "Memik Yanık sıfıra bölme hatasını bilemez" görüşünden yola çıkarak C# 3.0 kitabımda hata yakalama konusuna torpil geçip(tabi başka konuların sayfalarından kısarak) şimdiye kadar Türkçe yayınlanmış C# kitapları bağlamında bu kitabımın “Hata Yakalamak” konusunun en kapsamlı anlatıldığı kitap olmasını hedefledim. 

Şimdi birileri çıkıp “ey memik yanık hata yakalamak konusunda “en” kapsamlı bölümü yazmış olman yazdıklarını bildiğine delalet olmaz, hata yakalamak konusunda bilgi verirken hazırladığın 3-5 satırlık kodlarda kullandığın değişkenlere veya metotlara ad seçerken başka yerli yazarların kitaplarından “izinsiz” esinlenmediğini nereden bileceğiz” diye soru yöneltirlerse? Tam bu sırada ortaokul 3. sınıfta okurken tarih hocam aklıma geldi. Tarih dersinde pek iyi değildim. Baktım tarihten kalacağım, oturup biraz çalıştım. Son yazılı sınavda soruların hepsine cevap verip çıktım. Ertesi gün hocam beni tahtaya çıkarmıştı ve yazılıda sorduğu soruları bir de tahtada cevaplamamı istemişti. Benzer cevapları tahtada da verince bana tam puan vermişti ve kopya çekmediğime inanmıştı. Şimdi benzer çalışmayı yukarıda sözünü ettiğim Hata Yakalamak ve Exception Sınıfları makalesi için yapacağım. Başka bir deyişle bu makalede veya C# kitabımda yazdıklarımı bildiğimi kanıtlamaya, verdiğim örneklerde kullandığım değişkenlere, metotlara çalıntı yapmadan ad bulabilme becerisine sahip olduğuma, 2 sayıyı bir birine bölen C# kodu yazabildiğime sizleri inandırmaya çalışacağım. Tabii bunu yaparken konuları nasıl anlattığımı, hangi örneği neden verdiğimi vs tek tek anlatacağım. 

Hata Yakalamak mı Hata Ayıklamak mı? 

Her ne kadar C# 3.0 kitabımda Hata Yakalamak ve Hata Ayıklamak konularını ayrı birer bölüm olarak düzenlemiş olsam bile bu sitede sizlerle paylaştığım makalede hataları ayıklamaktan ve projeleri çalıştırmaktan kısaca söz ettim. Çünkü Visual Studio kullanan bir çok programcı adayı uygulamaların Debug ve Release modlarından çok sonra haberdar olabilmektedir. Entegre hata ayıklayıcısı ile ilgili olarak yapılmış ayarlamalardan Debug veya Release moduna göre Exception sınıflarının verdikleri tepkiler değiştiği için kısaca olsa bile Debug nedir, Release modu nedir sorularına cevap vermeye çalıştım. Bu düşünce ile bölümde bir sayfa boyunca dedikodu yaptıktan sonra “Projeleri Çalıştırmak” adında bir başlığa yer verdim.
 

Bazılarınız hemen “ey memik yanık bizlere projelerin nasıl çalıştırıldığını anlatmak için mi bu kadar hikâye okuttun?” diye tepki gösterebilirler. Bu soruyu soran arkadaşlara Visual Studio’nun menülerini tek tek dolaşıp Start veya Run adında bir komutun olup olmadığını araştırmalarını önermek isterim. Bir iki dakikalık araştırmadan çıkacak sonuç şu olabilir: Debug menüsünde Start Debugging adında bir komut var ve Standard araç çubuğunda bu komutu temsil eden bir düğme bulunmaktadır. 

Hemen sorumuzu soralım: Programcı üzerinde çalıştığı uygulamayı neden çalıştırmak ister? Cevap basittir ve nettir: Uygulamayı test etmek, varsa hataları ayıklamak için. İşte bu ihtiyaçtan yola çıkan Microsoft’un programcıları Visual Studio’nun menü çubuğunda Debug adında bir menüye yer verip bu menüde Start Debugging komutunun olmasına karar vermişlerdir. Makalede Debug ve Release modlarından söz ederken aşağıda verdiğim ekran görüntüsünü kullandım.



Bu ekran görüntüsünü kullanmamın nedeni üzerinde çalışılan uygulama Start Debugging komutu ile çalıştırıldığı zaman geri planda nelerin olduğunu, hazırlanan EXE dosyanın nereye konulduğunu okura göstermektir. Şimdi bazı arkadaşlar bu ekran görüntüsü makale veya kitapta fuzuli yer işgal etmez mi, bu ekran görüntüsünü kitapta kullanmakla kitabı şişirmiş olmuyor musun diye sorabilirler. Elbette bu ekran görüntüsü verilmese bile Visual Studio ile C# uygulaması geliştirmek isteyen kişi bir süre sonra Debug klasörüne konulan EXE dosyayı bir şekilde fark eder. Ancak bu fark etmenin anlamlı olması için aynı zamanda Bin klasörünün altında hazırlanan Release klasöründen de haberdar olması gerekir. Ötesi EXE dosyanın ne zaman Debug klasörüne, ne zaman Release klasörünü yerleştirildiğini bilmesi gerek. 

Bu ekran görüntüsünü kullanmanın amacı Visual Studio tarafından hazırlanan EXE dosyanın yerini göstermekten öte okurun konu üzerinde düşünmesini sağlamaktadır. İşini ciddiye alan yazar okurunun kafasından geçmesi muhtemel soruları öngörüp tam zamanında bu soruları cevaplar. Eğer yazar okurunun neyi bilip neyi bilmediği konusunda çok sık yanılırsa kitabı bazı okurlar için anlaşılmaz ve eksik olarak değerlendirilirken bazı okurlar için “bu kitabın düzeyi bana göre değilmiş” yorumu yapılır. Yine konu gelip hedef okur grubunu doğru seçip kitabın içeriğini ve anlatım tarazını ona göre seçmeye dayanıyor. 

Daha açık anlatımla Hata Yakalama veya Exception sınıflarını anlattığınız kişinin Release veya Debug modu nedir, Visual Studio’nun Configure Manager diyalog kutusunda neler ayarlanır, bu ayarların etkileri ve işlevleri nelerdir sorularına cevap verebiliyor olması gerekir. Bu konuların anlatıldığı kişi veya okur bu sorulara cevap veremiyorsa Exception sınıfları ilgili olarak anlatılanlardan “hiç bir şey anlamaz” denilemez ama kafasında oluşan bazı sorular cevaplanmamış olur. 

Okurun kafasında daha sonra cevaplanmak üzere(belki de hiç cevaplanmayacak) ertelenen soruların sayısı arttıkça yazılan metni okumak zorlaşır. Elbette kitabın ilk sayfasında sözünü ettiğiniz bir kavramı hemen orada anlatın demiyorum ama anlatmayacağınız veya çok sonra anlatacağınız kavramlardan mümkün mertebe söz etmemek gerekir. Örneğin birçok yerli yabancı yazar kitaplarının daha ilk sayfalarında Assembly, Application Domain, Process, Unmanaged Code vs.. daha birçok kavramın adını anarlar, birer cümle ile anlatmaya çalışırlar. Bana sorarsanız C# ve .NET Framework konusunda henüz yolun başında olan(zaten yolun başında olduğu için gidip kitap alıyor) okur için Application Domain veya Assembly’den birer cümle ile söz etmenin hiçbir yararı yoktur. Bırakınız yararı bazı temel kavramların 2-3 cümle ile anlatılması okurun kafasını karıştırıyor. Çünkü Assembly ve Application Domain gibi kavramlar hem ilk öğrenilmesi gereken konular değildir hem de en azından 15-20 sayfa boyunca anlatılması gereken konulardır. Yazar arkadaşlara bu konuda önerim şudur: Kitabınızın veya makalenizin hemen başında 2 cümle ile anlatmaya çalıştığınız kavram mutlaka bilinmeli midir? Bu soruya cevabınız evetse okurun o konuyu anlaması için ne gerekiyorsa hemen orada yapın. Okurun söz konusu kavram veya konuyu hemen öğrenmesi gerekmiyorsa ondan söz etmeyin. Ne zaman ki o konuyu ayrıntılı inceleyeceksiniz o zaman söz edin.

Try-Catch Blokları 

Projeleri çalıştırmaktan ve hataları ayıklamadan kısaca söz ettikten sonra sıra geldi kullanıcının çalışma anında yapması muhtemel hataları öngörüp tedbir almaya. Aslında benzer konuyu birçok kaynakta bulmak mümkündür. Zaten anlatılanlara fazla yabancı olmayan okur metni şöyle bir tarayıp verilen kod örneklerine bakar ve 3-5 satırlık kodlar kendisine yabancı değilse metni okumayı bırakır. Bu nedenle programcılıkla ilgili herhangi bir konu anlatılırken okurun o konudan bi haber olduğunu ve bilgisinin kitabınızın önceki sayfalarında yazılanlarla sınırlı olduğunu varsaymak zorunluluğu vardır. 

Ötesi okura bilmediği bir konuyu anlatılırken bilmediği başka bir konuya gönderme yapmamak gerekir. Örneğin C# kitabımda ve sözünü ettiğim makalede kullanıcı kaynaklı hataların yakalanması işleminin try-catch-finally blokları ile nasıl yapıldığını kısaca işaret ettikten sonra aşağıdaki gibi 2 cümle yazıp kısa bir kod verdim. C# 3.0 kitabımın Hata Yakalamak ve Exception Sınıfları adlı bölümünden yaptığım alıntıları italik yaptım. 

Kullanıcıdan bir işlem için tarih istediğinizi varsayalım. Kullanıcı TextBox’a tarih olarak değerlendirilemeyecek bilgi girerse 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);    
 }

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ı tarihsel tipe dönüştürülemeyecek bilgiyi TextBox’a girip başka nesnenin üzerine giderse bu kod hata verir ve program çalışması kırılır. 

Tabii ki bu kod son derece basittir ve okur her şartta DataTime’ın bir veritipi olduğunu fark eder. Ötesi ToDataTime() metodunu ilk kez gören birisi bile bu metodun işlevini yazarın desteği olmadan da anlar. En ideali madem hata yakalama işlemini anlatıyoruz ve okurun kitabı ilk sayfadan itibaren okuduğunu varsayıyoruz, bu kısacak kod verilmeden çok önce okurun DateTime yapısı hakkında bilgi sahibi olması gerekir. Başka bir deyişle Hata Yakalama bölümünden önce kitapta tarih ve zaman bilgileri üzerinde yapılabilinen işlemlerin anlatılmış olması gerekir. 

Ya C#’ta hata yakalama işleminin try-catch blokları ile yapıldığını söyledikten hemen sonra veritabanlarına özel bir hatadan söz etmiş olsaydım? Kitapta hata yakalama konusu veritabanı işlemlerinin anlatıldığı bölümden sonra olsaydı bu tercih bir soruna neden olmazdı. Bu nedenle kitaplarda yeni bir konuya geçildiğinde yazılanların kolay anlaşılmasını sağlamak için örnekler herkes tarafından bilinen bir konu veya işlemden yola çıkılarak hazırlanır. Bazı kitap ve makalelerde öyle karmaşık örnekler veriliyor ki okur anlatılan konuyu anlamaktan öte verilen örneğe kafa yormak zorunda kalıyor. 

Madem hata yakalamayla ilgili olarak verdiğimiz kısa açıklama ve 2 satırlık kod 3-5 saniyede okunup anlaşılacak basitliktedir hemen bu koda try-catch bloğunu monte edebiliriz. Tekrarlamak gerekirse aşağıda verilen kodun benzerini her yerde bulmanız mümkündür. Arada bir fark varsa o da catch anahtar kelimesinin kullanıldığı satıra burada bir şey yazılmamış olmasıdır. Dolayısıyla bu 3 satırlık kodu birileri sahiplenirse, birileri çıkıp fikrimi çalmışsın derse ona gülümsemeyi ihmal etmeyin. 

private
void textBox1_Leave(object sender, EventArgs e) 
 
   DateTime Tarih; 
   try 
   
      Tarih = Convert.ToDateTime(textBox1.Text); 
   
   catch 
   
      MessageBox.Show("Girdiğiniz tarih veya saat yanlış"); 
      textBox1.Focus();  
   
  } 

Konu hakkında az çok bilgisi olanların tahmin edeceği gibi bu kod işletilip ilk TextBox’a uygun tarih girilmeyip ikinci TextBox’ın üzerine gidilirse FormatException denilen yani string bilginin DateTime tipine dönüştürülememesi hatası ile karşılaşılır. Ancak başlangıçta okura meydana gelmesi muhtemel hatanın C# derleyicisinin nazarında hangisi olduğunu anlatmayı tercih etmedim. Başka bir deyişle ilk başta amacım try-catch bloğunun nasıl işlediğini göstermek olduğu için bir çok yazarın aksine catch deyiminin kullanıldığı satıra başka bir şey yazmadım. Çünkü aynı anda hem Exception sınıflarını hem de try-catch bloğunun işleyiş şeklini anlatmak istemedim. Çünkü amacım konuları birbirinden yalıtıp okurun ilk okumada yazılanları anlamasını sağlamaktır. Bir çok yazar arkadaşın kolay okunma bağlamında yaptıkları en büyük yanlış, okurun bilmediği birden fazla konudan veya işlemden aynı anda söz etmeleridir. 

Yukarıda verdiğim kısacak kod hakkında ek bir cümle etmek istiyorum: Kullanıcı çalışma anında ilk TextBox’a ister bir şey yazmadan, isterse de tuttuğu futbol takımının adını yazıp öyle 2. TextBox’a geçsin aynı hata, yani FormatException hatası meydana gelir.

Exception Sınıfları 

Okura böylelikle try-catch bloklarının nasıl işletildiğini işaret ettikten sonra Exception Sınıfları kavramına geçilebilir. Ancak ben tercihimi başka türlü yaptım. Konunun başına dönüyorum ve yukarıda verdiğim koddaki try-catch deyimlerini silip hata meydana geldiği zaman projenin çalışmasının sona erdirilmesini sağlayacağım 

Bazı kaynaklarda konu try-catch olduğunda release ve debug modları hakkında bir iki cümle yazılmış olsa bile kimse okurun merakını gidermeyi tercih etmiyor. Eğer kurduğunuz cümleyi okuyan veya verdiğiniz örneği inceleyen okurun kafasında neler geçer, neleri merak eder vs gibi soruları kendisinize sorup bu sorulara kendinizce cevaplar vermiyorsanız yazdığınız metin Microsoft’un MSDN’deki metinlerden bir farkı olmaz. Başka bir deyişle yazdıklarınızda help metni tadı olur. 

Kullanıcı yukarıda verdiğim kısa koddaki try-catch bloklarını kaldırıp(yani hata yakalayan satırları kaldırırsa) projeyi entegre debugger’dan bağımsız çalıştırırsa ne olur? Başka bir deyişle Start Debugging veya Build menüsündeki komutlar sayesinde hazırlanan EXE dosyayı Visual Studio’dan bağımsız çalıştırmak isterse ne olur? 

Tabii ki okur projesini Visual Studio’nun Start Debugging komutu ile çalıştırıp formdaki ilk TextBox’a geçerli bir tarih bilgisini girmeden bir sonraki TextBox’ın üzerine giderse Debugger projenin çalışmasını durdurur. Ancak programcı hiçbir tedbir almadan formdaki ilk TextBox’ın Leave olayını temsil etmesi için aşağıdaki gibi bir metot hazırlayıp sonra projesini Visual Studio ile gelen entegre debugger’dan bağımsız çalıştırırsa ne olur? 

private
void textBox1_Leave(object sender, EventArgs e) 
 
   DateTime Tarih; 
   Tarih = Convert.ToDateTime(textBox1.Text);    
 }

Bu konuyu neden mi uzattım: Cevap açık, henüz yolun başında olan programcı projesini hazırlayıp kullanıcıya teslim ettiğinde Visual Studio ile gelen entegre debugger’ın olmayacağını ve hatalar karşısında programının farklı tepkiler verebileceğini bilmesi gereklidir. Bu nedenledir C# kitabımın bu bölümünde aşağıda verilen 2 paragrafa ve 2 ekran görüntüsüne yer verdim. 

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 “\bin\debug” klasörüne, Release modunda iken derleme yapıldığında ise EXE dosya “\bin\release” klasörüne konulmaktadır. Derleme işlemini yaparken Release modunda olduğum için derleme sonucu hazırlanan EXE dosya “\bin\Release” 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. Kullanıcı 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 erdirebilir. Meydana gelen hata hakkında ayrıntılı bilgi edinilmek istendiğinde ise Details düğmesi tıklanır.



Davrandığı hatırlatıldıktan sonra sıra hata yakalama sınıflarını anlatmaya geldi .NET Framework ile gelen hata yakalama sınıfları konusunun değişik boyutları olduğu için hata üretmesi muhtemel farklı kodlar yazıp aynı konuyu tekrarlama yoluna gittim. Bu düşünce ile forma bir ListBox yerleştirip aşağıda verdiğim kodu yazdım.

string[] takimlar = new string[5];
int adet = listBox1.Items.Count;
for (int i=0; i<adet; i++) 
   takimlar[i]=listBox1.Items[i].ToString(); 

Bu kodun anlaşılmaz bir yanı yoktur. Tabi bu değerlendirmeyi bu kitabın hata yakalama konusunun anlatıldığı sayfalardan öncesini okumuş olanlar için yapıyorum. Bu kodda önce 5 elemanlı string bir değişken tanımlanıp devamında for döngüsü ile mevcut ListBox’ın mevcut elemanları bu dizi değişkene aktarılmaktadır. 

Yukarıda okurun karşılaşmasını istediğim FormatException hatasından başka programcıların sık yaptıkları hatalardan bir diğeri dizi değişkenlerde ve indeksleyicisi olan sınıflarda yapılan IndexOutOfRangeException hatasıdır. Madem bu hata sık yapılıyor üzerinde durmak gerekiyor. Kitabımda hataya neden olması muhtemel bu kodu aşağıdaki gibi try-catch bloğuna aldım. 

private
void Form1_Click(object sender, EventArgs e) 
 
   string[] takimlar = new string[5]; 
   int adet = listBox1.Items.Count; 
   try 
   
      for (int i = 0; i < adet; i++) 
        takimlar[i] = listBox1.Items[i].ToString(); 
   
   catch 
   
      MessageBox.Show("Dizi Değişkenin sınırı aşıldı\n"
           "Yalnızca ilk 5 eleman diziye aktarıldı"); 
   
 

Koda dikkat edilirse catch deyiminin kullanıldığı satırda catch’den başka bir şey yoktur. Bu catch örneğinden sonra okurun çalışma anında hataya neden olabilecek satırları nasıl catch bloğuna alması gerektiğini kavradığına inanabilirsiniz. Burada da hataya neden olması muhtemel satırlarda okurun yabancısı olmadığı bir tek işleme yer verdim. Ötesi şimdiye kadar try-catch bloklarıyla ilgili olarak yalnızca şunu yazdım:Çalışma anında try bloğundaki herhangi bir satır hataya neden olursa programın işletimi hemen catch bloğuna geçer ve programın çalışması kırılmaz. 

Artık herhangi bir hata meydana geldiğinde programın işletimi try bloğundan catch bloğuna geçtiğinde neler oluyor sorularına cevaplar verebiliriz. Bu düşünce ile yukarıda verdiğim kodu aşağıdaki gibi değiştirdim. 

private void Form1_Click(object sender, EventArgs e) 
 
  string[] takimlar = new string[5]; 
  int adet = listBox1.Items.Count; 
  try 
  
    for (int i = 0; i < adet; i++) 
      takimlar[i] = listBox1.Items[i].ToString(); 
   
  catch(IndexOutOfRangeException Hata) 
 
    MessageBox.Show("Meydana gelen hatanın mesajı:\n"+ Hata.Message); 
  }
}

Bazı okurlar muhtemelen itiraz edip diyeceklerdir ki, böyle adım adım anlatmak, okuru ikna etmeden başka konuya geçmemek sayfa sayısını arttırmaz mı? Elbette sayfa sayısı artar. Hem okuru konuya hazırlayacaksınız, hem kafasında oluşması muhtemel soruları öngörüp tek tek cevap vereceksiniz, hem okur saçını başını yolmadan yazılanları kolayca okuyacak, hem okur yazılanları ikinci kez okumak zorunda kalmasın diyeceksiniz hem de metin kısacık olacak. Tabii bu mümkün değildir. Yani yazar arkadaşımız tercihini yapacak: Ya anlatmak istediği konunun bütün boyutlarını içeren bir örnek verip onun üzerinde birkaç cümle yazıp başka hemen konuya geçecektir ya da anlatmak istediği konuyu alt konulara bölüp her seferinde okura bu alt konulardan birisini anlatacaktır. 

Tam bu noktada
catch(IndexOutOfRangeException Hata) satırı üzerinde biraz durmak ve tek başına catch yazılan satırdan farkını anlatmak gerekir. Kitabımda bu amaçla şu cümleye yer vermiştim: 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. Demek ki try bloğunda nasıl bir hata meydana gelirse o hatayla ilgili sınıfın örneği alınıp catch bloğuna gönderiliyor. Eğer catch bloğunun ilk satırında catch deyiminden sonra parantezlerin içinde söz konusu sınıf tipinde bir değişken tanımlanmışsa o hata yakalanıyor. Daha doğrusu meydana gelen hatayı temsil eden sınıfın örneği catch deyiminin olduğu satırda tanımlanan değişkene aktarılıyor. Zaten verdiğim kodda bilerek bu nesnenin Message özelliğinin içeriğine baktım. Konunun hemen devamında okurun ilgili sınıfın örneğinin alınıp catch bloğuna gönderilmesi işlemini hemen kavramaması ihtimali yüksek olduğu için daha önce sizlerle paylaştığım Hata Yakalamak ve Exception Sınıfları adlı makaleye aşağıda verdiğim 2 paragrafı ve kısa kodu ekledim. 

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 yukarıdaki sayfalanda verdi­ğim örneği aşağıdaki gibi düzenledim. Değişken adı çalma suçumu(!) hafifletmek için DateTime tipindeki değişkene “Tarih” yerine “History” adını verdim


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 var 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. 

Bu açıklamadan sonra artık okura birden fazla catch bloğundan söz edilebilir. Bu düşünce ile birden fazla hataya neden olabilecek basit bir kod hazırladım. Aşağıda verdiğim kodda TextBox1 ve TextBox2’nin içeriğini Convert sınıfının ToByte() metodu ile dönüştürüp Number1 ve Number2 adını verdiğim byte tipindeki değişkenlere aktardım. Tanımladığım bu değişkenker Byte tipinde olduğu için bu TextBox’lara girilen bilgi 255’ten büyük olduğunda OverflowException hatası ile karşılaşılır. Normalde bu değişkenlere alışkanlıklarım gereği Sayi1, Sayi2 gibi adlar verirdim. Değişken adı benzerliği kabahatini tekrar işlememek için “Sayi” yerine Number’ı tercih ettim. 

private void Hesapla_Click(object sender, EventArgs e) 
 
   byte Number1 = Convert.ToByte(textBox1.Text); 
   byte Number2 = Convert.ToByte(textBox2.Text); 
   float Number3 = Number1 / Number; 
   textBox3.Text = Number3.ToString();   
 

Benzer şekilde bu 2 TextBox’a ToByte() metodu ile Byte tipine dönüştürülemeyecek bilgi girilirse FormatException hatası meydana gelir. Tabi kodun devamında Byte tipindeki bu 2 değişkenin içeriği birbirine bölündükleri için sıfıra bölme yani DivideByZeroException hatası meydana gelebilir. Bu kısacak kodda 3 hatanın meydana gelmesi muhtemel olduğu için okura Exception sınıfları hakkında bilgi vermek artık kolaylaşmıştır. Bazı yazarlar benzer bir işlemi, bir metodu veya sınıfı anlatırken örnek verirken özenli davranmazlar. Dolayısıyla verdikleri örnekle ilgili olarak okurun kafasında beliren soruları öngörmeye çalışmazlar. Bu nedenledir ki bazı arkadaşlar yukarıda verdiğim basit kodu küçümserler. Sanki karışık örnek verdiklerinde okurların kendilerine saygısı artacakmış… 

Yukarıdaki sayfalarda amaç try-catch blokları hakkında bilgi vermekti. Tabi bu yapılırken ister istemez birkaç Exception sınıfından söz etmek gerekiyordu. Madem Exception sınıflarının nasıl kullanıldığından söz ettik artık .NET Framework ile hazır olarak gelen ve sık kullanılan bazı Exception sınıflarından söz edebiliriz. Tabi .NET Framework ile birlikte çok sayıda Exception sınıfı hazır olarak geldiği için hepsini anlatmak, hepsinden söz edip örnek vermek mümkün değildir. Ötesi bazı Exception sınıfları ancak ilgili oldukları konu ile paralel anlatıldığı zaman anlaşılmaktadır. Örneğin henüz Thread’ler hakkında bilgi sahibi olmayan okura Thread’lerle ilgili Exception sınıfından söz etmek anlamlı değildir. Gelin InvalidCastException ve
NullReferenceExceptionsınıfından nasıl söz ettiğimi birlikte inceleyelim: 

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. Bu kodda “Nesne” adı verilen 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 Nesne = 2008;
try 
 
   int Yil = (int)Nesne; // Tehlikeli bir değişken adı 
   this.Text = Yil.ToString(); 
 }
catch (System.InvalidCastException Hataspor) 
 
   MessageBox.Show("Dönüştürme hatası meydana geldi" + n\r" + Hataspor.Message); 
 

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ı. Başka bir deyişle object tipindeki değişken int tipindeki değişkene aktarılamayacak bilgi içeriyor olsaydı dönüştürme işlemi başarısız olurdu. 

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” başka yerli yazarlar tarafından daha önce register edilmiş(!) değişken adları 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ış"); 
 

Şimdi bazı okurlar diyecekler ki bu 2 sınıfı anlatmak için verdiğin kısacak örneklerde ne var? Gerçekte bu 2 örnekte orijinal bir şey yok. Madem bu kısa örnekler bu 2 sınıfın işlevini göstermek için verildiğinde okur bir sayfalık bu metni okuduğunda
InvalidCastException ve NullReferenceExceptionsınıfları hakkında ilk okumada bilgi sahibi olur mu olmaz mı ona bakmak gerekir. Ötesi bu Exception sınıflarıyla ilgili olarak verilen örneklerde anlaşılmaz bir yan yoktur diyorsanız sorun yoktur.


Finally Bloğunu Nasıl Anlattım 

Yukarıda anlatılan şekilde try-catch bloğunun işleyişi hakkında okuru bilgilendirdikten sonra finally bloğundan kısaca söz ettim. Neden mi finally bloğundan çok sonra söz ettim? Nedeni son derece basit : Okura yabancısı olduğu birden fazla konuyu aynı anda anlatmama kaygısı bu tercihimde etkili oldu. Yoksa bir örnekte try-catch-finally blokları bir seferde anlatılır. Tabi bu durumda henüz yolun başında olan, daha doğrusu hata yakalama ve Exception sınıfları hakkında yeterli bilgisi olmadığı için kitap edinen, makale okuyan okur yazılanları anlamakta zorluk çekecekti, belki yazılanları sonuna kadar okumayacaktı. Finally bloğu hakkında bilgi verirken aşağıdaki gibi basit bir örnek hazırladım. 

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;      
 

Dikkat edilirse bu kısacak kodda ne try-catch ne de finally bloğu var. Niye mi her 3 bloğu bir seferde hazırlayıp vermedim? Amaç konuyu adım adım anlatıp ilk okumada anlaşılmasını sağlamaktır. Ötesi okurun bildiği bir konudan hareketle yeni bir konu anlatılmak istenmektedir. Eğer finally bloğunun işlevini henüz kavramamış okura finally bloğu ile Dispose işleminin yakınlığından söz etmiş olsaydım Dispose() metodu ve Dispose işlemi hakkında yeterince bilgisi olmayanların kafasını karıştırıp finally bloğunu anlatabilme şansını zora sokardım. Ötesi dispose işlemi hakkında ayrıntılı bilgisi olan okur zaten finally bloğunun ne işe yaradığını biliyordur. Bu kodu verdikten sonra kitapta aşağıda verdiğim 2 paragrafa ve koda yer verdim. 

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 yukarıda verdiğim 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şletilip kullanıcıya 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. Burada 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; 
    
   } 

Bu anlatılanlara göre ister hata meydana gelsin ister gelmesin finally bloğuna her şartta işletilmesi istenen satırlar yazılmalıdır. Her ne kadar konuyu adım adım anlatmış ve bazılarına göre uzatmış olsam bile finally bloğu hakkında yazılanları okumak fazla zaman almaz. Okur kısa sürede finally bloğunun işlevi hakkında bilgi sahibi olur. Tabi buradaki asıl amaç finally bloğunun ne zaman ve nasıl işletildiğini anlatmaktır. Yoksa finally bloğunda yapılabilinecek işlemleri tek tek saymak değildir.

Genel 09.10.2009 11:40:41

YORUM YOK


YorumlarYorum Yaz
Ana Sayfa | Hakkımda | Kitap Önerileri | Fotoğraflarım | RSS | İletişim
Memik Yanık 2004-2009 © Tüm Hakları Saklıdır.
Hazırlayan www.semgoksu.com