C# İle biLGİsayar programlama temelleri (C# Programlama Kitabı) Svetlin Nakov & Co



Yüklə 3,36 Mb.
səhifə30/31
tarix03.11.2017
ölçüsü3,36 Mb.
#28822
1   ...   23   24   25   26   27   28   29   30   31

12.3 İstisna Hiyerarşisi


.NET Çerçevesi’nde iki tür istisna vardır: Geliştirdiğimiz uygulamalar içinden fırlatılan istisnalar (ApplicationException) ve çalışma zamanında fırlatılan istisnalar (SystemException).



canvas 43
İstisna sınıflarının hiyerarşisine temel oluşturan bu sınıfların her biri farklı özelliklere sahiptir.

12.3.1 Exception Sınıfı

.NET Çerçevesi içerisinde Exception sınıfı programın yürütülmesi sırasında ortaya çıkan neredeyse tüm istisnalar için temeldir. ApplicationException ve SystemException dahil birçok sınıf doğrudan bu sınıftan kalıtımsal türetilir.


Exception sınıfı istisna oluştuğu anda bir kopyasını çağrı-yığıtında saklar. İstisnayı fırlatan metot genellikle hatayı anlatan kısa bir mesaj içerir. Her istisna içteki istisna, sarmalı istisna yada iç istisna gibi adlandırılan iç içe geçmiş bir istisna olabilir.
Bir istisnayı bir başka istisna ile sarmalamak tekniği bazı durumlarda çok kullanışlıdır ve istisnaların istisna zinciri ile bağlı olmalarına olanak verir.

12.3.2 İstisna – Kurucular, Metotları ve Özellikleri



System.Exception yapısı aşağıdaki gibidir:

[SerializableAttribute]

[ComVisibleAttribute(true)]

[ClassInterfaceAttribute(ClassInterfaceType.None)]

public class Exception : ISerializable, _Exception

{

public Exception();



public Exception(string message);

public Exception(string message, Exception innerException);

public virtual IDictionary Data { get; }

public virtual string HelpLink { get; set; }

protected int HResult { get; set; }

public Exception InnerException { get; }

public virtual string Message { get; }

public virtual string Source { get; set; }

public virtual string StackTrace { get; }

public MethodBase TargetSite { get; }

public virtual Exception GetBaseException();

}



Exception sınıfının yukarıda verilen tüm özelliklerini anlatmak oldukça karmaşıktır, bu nedenle yalnızca en önemli metotlarını ve özelliklerini tartışacağız. .NET çerçevesi içerisinde bulunan tüm istisnalar bu sınıftan kalıtımsal türetilmiştir.


  • Program mesajları ve içteki istisnalar üç kurucunun farklı kombinasyonlarıyla kontrol edilir.

  • Message özelliğiyle istisnayı programcıya açıklayan bir metin döndürülür. FileNotFoundException istisnası için mesajda dosya bulunamadı bilgisi yer alır. İstisnayı fırlatan program kodu mesajı kurucuya iletir iletmez, mesaj içeriği değişmez.

  • InnerException özelliği içteki istisnayı (sarmalı, iç içe geçmiş) döndürür, yada böyle bir mesaj yoksa null döndürür.

  • GetBaseException() bir istisna zincirinde bulunan en içteki istisnayı döndürür. Tanım olarak, bir istisna zinciri içerisinde bulunan istisnalar bu metot sonucunda aynı davranışı gösterir.

  • StackTrace özelliği istisna için yığıtta saklanan tüm bilgiyi döndürür (yığıt görünümünü daha önce açıklamıştık).



12.3.3 Uygulamadaki İstisnalar ve Sistem İstisnaları

İstisnaları .NET içerisindeki sistem istisnaları ve uygulama istisnaları olmak üzere iki kategoride inceleyeceğiz. Sistem istisnaları .NET kütüphanelerinde programlı gelir ve .NET çerçevesi tarafından kullanılır. Uygulama istisnaları uygulama yazılımı geliştirmek isteyenler tarafından kullanılır. Bu tür istisnaların doğrudan SystemException sınıfından değil, ancak ApplicationException sınıfından türetilmesi önerilir. SystemException sadece .NET Çerçevesi içinden kalıtımsal türetilir.


Sistem istisnalarının en tehlikelileri uygulamamızın doğru davranışına engel olur. Bu hataların bazıları ExecutionEngineException (CLR iç hatasının fırlatılması), StackOverflowException (çağrı-yığıtı, büyük olasılıkla yanlış programlama nedeniyle özyinelemenin sonlandırılmaması ve yığıtın taşması), OutOfMemoryException (yetersiz bellek) olarak kaydedilmiştir. Sık sık böyle bir durum oluşursa, uygulama çöktü anlamına gelmesi nedeniyle uygulamanın kendisini düzeltmesi zaman alacaktır.
Dışsal bileşenler (COM bileşenleri gibi) ile etkileşime giren istisnalar ExternalException sınıfından türetilir, örneğin COMException ve Win32Exception.


12.4 İstisna Fırlatmak ve Yakalamak

Bu bölümün açıklamalarında istisna fırlatılması ve yakalanmasının detaylarını bulacaksınız.



12.4.1 İç içe İstisnalar

OOP uygulamalarında hata işleme teknikleri arasında sarmalama yaygın olarak kullanılmaktadır. Her istisnai durum iç içe geçmiş (içteki) istisna olabilir.


Uygulama istisnalarını yazılım mühendisliği gereksinimleriyle belirli az sayıda istisnayla sınırlamak iyi bir uygulamadır. Bunun avantajı kullanıcıların yazılım bileşeninden ne bekleyeceklerini önceden bilmelerine olanak tanımasıdır.
Bankacılık yazılımı geliştiren bir programcının faizlerle ilgilenen bir yazılım bileşenine ihtiyacı olduğunu varsayalım. Bu bileşen InterestCalculationException ve InvalidPeriodException gibi istisnaları tanımlar ve fırlatır. FileNotFoundException, DivideByZeroException ve NullReferenceException gibi istisnaları tanımlamaz ve fırlatmaz. Faiz dışında herhangi bir hata olursa, bu hata sarmalanır ve faiz mesajı iletilir. Sarmalamayı InterestCalculationException sınıfını tanımlayarak adlandırabilirsiniz.
Bir örnek daha vermek gerekirse, A adıyla kendi uygulama istisnalarını tanımlayan bir yazılım bileşeni A-istisnalarını fırlatıyor. Bu bileşen içsel olarak bir başka B bileşenini kullanırsa, ve B bileşeni yine kendi içinde tanımlanan B istisnalarını fırlatırsa, A bileşeni görevini yerine getiremeyebilir ve belki de oluşan hata A üzerinden istenmeyen yerlere yayılır. A bileşeni B-istisnasını fırlatamayacağı için, iç içe geçmiş bir B istisnası halinde A- istisnası fırlatacak.

Bileşen uygulamadaki bir istisnayı fırlattığında, hata teknik detaylarına ancak içteki istisna olursa bağlı kalacaktır diye varsayılmıştır. Sebepleri arasında yazılım mühendisliğinde sıkça tanımlanan iş uygulamalarının istisnai gerçekler hakkında doğru ve tam yansıtma yapmadıkları iddiası bulunmaktadır.


Herhangi bir A bileşeni neden kendisi dışında aynı uygulamadan olsun veya olmasın herhangi bir B bileşeni için tanımlı istisnayı fırlatamaz? Sebepleri şöyle sıralanmıştır:


  • A bileşeni kullanıcıları B bileşeninin var olduğundan haberdar olmak zorunda değildir (bkz. “Nesne Yönelimli Programlamanın İlkeleri” Bölümü’nde soyut kavramlarla ilgili tartışmalar).

  • A bileşeni B istisnalarını fırlatabileceğini bildirmemiştir.

  • A bileşeni kullanıcıları B istisnalarını almaya hazır değildir. Sadece A bileşeninin istisnalarını beklemektedir.



12.4.2 İç içe İstisnalarla Kayıtlı Yığıt İzlencesi Nasıl Okunur?

Bu bölümde bir istisna zincirinin nasıl oluşturulacağını ve kayıtlı yığıt izlencesini göstereceğiz. Böyle bir istisna zincirini oluşturan örnek aşağıda verilmiştir:



37

38

39



40

41

42



43

44

45



46

47

48



49

50

51



52

53

54



55

static void Main()

{

try



{

string fileName = "WrongFileName.txt";

ReadFile(fileName);

}

catch (Exception e)



{

throw new ApplicationException("Smth. bad happened", e);

}

}

static void ReadFile(string fileName)



{

TextReader reader = new StreamReader(fileName);

string line = reader.ReadLine();

Console.WriteLine(line);

reader.Close();

}


Bu örnekte WrongFileName.txt diye bir dosya okunmak isteniyor. ReadFile() metotunun çağrılmasıyla (satır 42) program bir istisna fırlatır (satır 51) çünkü böyle bir dosya bulunmuyor. Main() metotunda istisnaların tamamını (satır 44) yakalayabilirsiniz. ApplicationException sarmalamasıyla bu istisnalar yeniden fırlatılıyor (satır 46). “Farklı Hata Türlerinin Gruplandırılması” Bölümü’nde daha sonra göreceğimiz gibi, önbelleğe alınan bir istisna hiyerarşinin alt katmanlarında bulunan diğer tüm istisnaları yakalar. Her istisna fırlatıldıktan sonra, .NET Çerçevesi tarafından yakalanıyor ve yığıt izlencesi konsolda görüntülenir.


Yukarıdaki örnek çalıştırıldığında sonucu aşağıdaki gibidir:

Eğer daha yakından ilk satıra bakarsanız, aşağıdaki biçimde bilgi içerdiğini fark edeceksiniz:

Unhandled Exception: Exception1: Msg1 ---> Exception2: Msg2
T

Burada Exception1 türü bir istisna Exception2 türü bir istisna tarafından sarmalanmıştır.


Yığıt izlencesine tekrar bakalım. İç içe istisnaların sonunu belirten bir ek bölüm görüyorsunuz:


--- End of inner exception stack trace ---

Bu işaret yığıt izlencesini sonlandıracaktır. Her istisna için yığıt izlencesindeki bilgileri kullanarak istisna mesajını görebilir ve istisnanın nasıl ve nerede oluştuğunu bulabilirsiniz (Dosya adı, metotu, satır numarası ve Message özelliği bu bilgileri bulmada yardımcı olacaktır).



12.4.3 İstisnaların Görselleştirilmesi



Konsol uygulamalarında, kullanıcıya hataları bildirmenin kolay kullanımlı bir yolu olmasa da hata mesajları genellikle çıktıya yazdırılır.
Web uygulamalarında, hatalar sık sık sayfanın alt kısmında veya başlangıçta yada hata ile ilgili kullanıcı arabirimi öğesinin hemen yanında gösterilir.
GUI uygulamalarında, kolay anlaşılır bir hata açıklamasını içeren iletişim penceresinin hataları göstermesi gerekir. İletişim kutusunda gösterilen hata iletisine bir örnek aşağıda verilmiştir:

Gördüğünüz gibi uygulama türüne ve hedef kitlesine bağlı olarak değiştiği için istisnaları işlemek ve görselleştirmenin tek ‘doğru’ yolu yoktur. Buna rağmen, istisnaları işlemeye ve bunları en iyi şekilde kullanıcılara göstermeye ilişkin bazı öneriler hala vardır. Bu tavsiyeleri “İstisnaların Kullanılmasında En İyi Alışkanlıklar” Bölümü’nde tartışacağız.



12.4.4 Hangi İstisnalar İşlenmeli ve Hangileri İşlenmemeli?

İstisna işlemeye ilişkin tek bir evrensel kural vardır.





Bir metot sadece beklediği ve işleyişini bildiği istisnaları ele almalıdır. Diğer tüm istisnalar çağıran metota terk edilir.

Bu kuralı takip edersek ve her metot halledemeyeceği istisnaların işlenmesini onu çağıran metota bırakırsa, en sonunda Main() metotuna (veya ilgili yürütme parçacığını başlatan metota) ulaşılır ve eğer bu metot istisnayı yakalamazsa CLR konsolda hata görüntüleyecek (veya başka bir şekilde hatayı görselleştirecek) ve program sonlandırılacaktır.


Bir metot eğer istisnayı bekliyorsa, neden oluşturulduğu ve bu durumda ne yapacağı hakkında bilgisi varsa ancak bu istisnayı işleme yeteneğine sahip olur. Örneğin, eğer bir metin dosyasını okuyan ve içeriğini bir dize olarak döndüren bir metotumuz varsa, bu metot FileNotFoundException istisnasını yakalayabilir ve bu durumda, boş bir dize döndürür. Yine de bu aynı metotun OutOfMemoryException istisnasını doğru olarak ele alması pek mümkün olmayacaktır. Yetersiz bellek durumunda bu metot ne yapmalıdır? Boş bir dize mi döndürür? Başka bir istisna mı fırlatır? Tamamen farklı bir şey mi yapar? Görünüşe göre bu metot bu istisnayı işlemeye yetkili değildir ve bu nedenle en iyi yol çağıran yönteme istisnayı geçmektir ve böylece istisna bunu yapmak için yetkili, farklı bir düzeydeki metot tarafından ele alınabilir. Bu basit felsefeyle, istisna işlenmesi planlı ve sistemli bir şekilde sağlanır.

12.4.5 Main() Metotundan İstisna Fırlatılması – Örnek



Main() metotundan istisna fırlatmak genellikle iyi bir uygulama değildir. Bunun yerine, Main() içerisinde istisna yakalamak daha iyidir. Yine de tabii ki Main() içinden istisna fırlatmak, başka herhangi bir metottan olduğu gibi mümkündür:

static void Main()

{

throw new Exception("Ooops!");



}


Main() içerisinde işlenmeyen her istisna sonunda CLR tarafından yakalanır ve yığıt izlencesi konsol çıktısında veya başka bir şekilde görüntülenir. Küçük uygulamalar için bu bir sorun olmamakla birlikte, büyük ve karmaşık uygulamalar genellikle zoraki çöküş yaşamamalıdır.

12.4.6 Farklı Düzeyde İstisnaların Yakalanması – Örnek

İstisnaları belirli bir metottan onu çağıran üst metota geçirmek (aktarmak), birden çok düzeyde özel durum işlemesini yapılandırmaya olanak tanır. Bu, belirli bir metot içerisinden belirli türdeki istisnaları yakalayabiliriz anlamına gelir ve diğer tüm özel durumları çağrı-yığıtı içinde önceki seviyelere iletiriz. Aşağıdaki örnekte, ReadFile() metotu istisnaları iki seviyede ele alıyor (ReadFile() metotu içindeki try-catch bloğu ve Main() metotu içindeki try-catch bloğu):



static void Main()

{

try



{

string fileName = "WrongFileName.txt";

ReadFile(fileName);

}

catch (Exception e)



{

throw new ApplicationException("Bad thing happened", e);

}

}
static void ReadFile(string fileName)



{

try


{

TextReader reader = new StreamReader(fileName);

string line = reader.ReadLine();

Console.WriteLine(line);

reader.Close();

}

catch (FileNotFoundException fnfe)



{

Console.WriteLine("The file {0} does not exist!",

filename);

}

}


Bu örnekte ReadFile() metotu sadece FileNotFoundException istisnasını yakalar ve işler. Diğer tüm özel durumlar Main() metotuna iletilir. Main() metotu içerisinde sadece IOException türü istisnalar işlenir ve diğer tüm istisnaları CLR halleder (eğer, örneğin programın yürütülmesi sırasında OutOfMemoryException istisnası atılırsa, CLR tarafından ele alınacaktır).


Main() metotu yanlış bir dosya adı geçerse, ReadFile() içinde TextReader başlatılırken FileNotFoundException istisnası fırlatılır. Bu istisna ReadFile() metotunun kendisi tarafından ele alınacaktır(). Öte yandan dosya geçerliyse, ama bir okuma sorunu varsa (yetersiz izinler, bozuk dosya içeriği vb.), fırlatılan ilgili istisna Main() metotu tarafından işlenecektir.
Belirli bir hata için farklı seviyelerde istisnaları işlemek hata durumlarının en uygun yerde işlenmesine olanak tanır. Program kodunun açık ve yapılandırılmış olmasını sağlar ve elde edilen esneklik çok büyüktür.



Yüklə 3,36 Mb.

Dostları ilə paylaş:
1   ...   23   24   25   26   27   28   29   30   31




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©muhaz.org 2024
rəhbərliyinə müraciət

gir | qeydiyyatdan keç
    Ana səhifə


yükləyin