C# Exception Yapısı ve Custom Exception

5 Şub

Yazılım geliştirici olarak yazdığımız kodlarda oluşabilecek istenmeyen durumlar ile başa çıkmak durumundayız. Aksi taktirde yazılımlarımız zamanla kullanılamaz hale gelebilir. İstisnai durumların oluşturulması veya oluşturulmaması üzerine zamanla bazı fikirler gelişmiştir.

Microsoft tarafından oluşturulan eski yönergelerde istisnai durumlar ile başaçıkabilmek için bazı yönlendirmeler bulunmaktadır. Bunlardan biri de temel Exception sınıfından türetilmiş SystemException ve ApplicationException şeklinde iki kategori oluşturarak istisnai durumları gruplamaya çalışmaktır.

ApplicationException

ApplicationException sınıfı System.Exception sınıfından türetilmiş bir istisnai durum bildirim türüdür. Bu türü, uygulamamızın iç yapısı ile alakalı istisnai durumlar oluştuğunda bildirimler oluşturmak amacıyla oluşturulmuştur. İstisnai durumlar bir hata veya bir uyarı olabilir. Uygulamamıza özel istisnai durum sınıflarına ihtiyaç duyduğumuzda ApplicationException sınıfından durumumuza özel yeni bir sınıf türetebiliriz. Özel istisna (custom exception) sınıfları oluşturma konusunda Microsoft tarafından oluşturulan kılavuzlarda tecrübe aktarımları mevcuttur.

SystemException

SystemException sınıfı da System.Exception sınıfından türetilmiştir. .Net Framework üzerinde CLR tarafından üretilen tüm istisnalar bu sınıf altında toparlanmıştır. Bu sınıf ile sisteme ait istisnai durum bildirimleri oluşturulmaktadır ve fetal error diye adlandırılan önemli hataların oluştuğu istisnai durumlarda kullanılırlar. Örneğin veritabanı bağlantı hataları, dosya hataları, bellek kullanım hataları oluştuğu durumlarda SystemException sınıfından türetilmiş olan sınıflar kullanılır.

İstisnai durumların kod ile ayırımı

Kodlama sırasında try-catch bloğunda istisnai durumun türüne göre tavrımızı belirleyebiliriz.

   try{
       var data = fileService.ReadFromFile();
   }
   catch(ApplciationException e){
       // Bu kısımdaki hata kontrolüm altında devam et...
   }
   catch(SystemException e){
       // Bu hata sistemsel bir sorundur log tut ve tekrar istisna gönder
       throw;
   }

SystemException türünde bir durum meydana geldiyse bu hatalar mutlaka log servisleri ile kayıt altına alınmalıdır ve görmezden gelinmemelidir.

Runtime esnasında oluşan hataları System, uygulama tarafında oluşan istisnaları Application olarak ayırmak mantıklı gibi görünebilir. Ancak bir istisnanın ölümcül(fetal) olarak değerlendirilmesi istisnanın içinde bulunduğu şartlara bağlıdır. Çalışma zamanında oluşan her hatanın ölümcül(fetal) olduğu söylenemeyeceği gibi framework dışındaki geliştiricilerin yazdığı uygulama istisnaların hepsinin göz ardı edilebilir olduğu söylenemez.

Örneğin FileNotFoundException durumu oluştuğunda her zaman programı kapatmanın anlamı yoktur. Bu durum kullanıcıya uygun bir mesaj ile iletilebilir. Hatta bazı dosyaların eksikliğinin öngörülebilmesi için faydalıdır bile denilebilir.

Bu nedenle bu başlangıç kılavuzu çıkmadı ve Microsoft da zaten çıkarmak için zorlamadı. Öte yandan şu anda .Net Framework’te bile ApplciaitonException sınıfından türetilmiş birkaç istisna bulabilirsiniz. Günümüzde yeni bir istisna türü oluşturmak isteyen biri bu iki kategoriyi kullanmak yerine Exception temel sınıfından yeni bir istisna sınıfı oluşturmaktadır. Yani System ve Application kategorisini kullanan pek yoktur.

Özel istisna türleri (custom exceptions) oluşturmak

Exception durumları hakkında kısa bir zaman yolculuğu yaptıktan sonra asıl konumuz olan özel istisna sınıflarının oluşturulmasına gelmiş bulunuyoruz.

Günümüzde geliştiricilerin kendine özel istisna türlerini oluşturmak için belli bir hiyerarşi oluşturduğunu görmek mümkündür. Örneğin:

Burada iş kurallarına ait tüm istisnalar BusinessRuleException altında toplanmıştır.

Peki bu durumda özel istisna sınıfları oluşturmak uygun bir yöntem midir? Ya da InvalidOperationException, ArgumentException gibi özel türleri mi kullanmak gereklidir? Bu iki sorunun cevabı istisnaları kimin kullanacağıdır. Kullanacak olan sizseniz özel istisnalar oluşturmak korkunç bir düşüncedir. Burada unutulmamalıdır ki istisnalar kodda istisnai bir durumu belirtmek için kullanılır. Yani beklenmeyen bir durumu anlatmak için kullanılır.

Özel istisnalar ile ilgili bir kod yapısı oluşturulduğunda aşağıdaki gibi bir durum meydana gelecektir.

public string CreateCustomer(string name, string email)
{
   try
   {
     /* Creating a customer */
   }
   catch (EmailIsNotUniqueException)
   {
       return "The email provided already registered: " + email;
   }
   catch (CustomerNameIsInvalidException)
   {
       return "Customer name is invalid: " + name;
   }
   catch (CustomerAddressIsInvalidException)
   {
       return "Customer address is invalid";
   }
}

Bu kod yapısı C# diline uygun olsa da, hatta kullanışlı görünse bile iyi bir kullanım şekli değildir. Çünkü burada program akışı kontrol edilirken istisnalar kullanılmıştır ve istisnaların kontrol akışını sağlamak gibi bir görevi yoktur. Yukarıda da bahsettiğimiz gibi istisnalar istenmeyen bir durumu belirtmek için kullanılır.

Yazdığınız kodu sadece siz kullanacaksanız özel istisna sınıfları türetmenize gerek yoktur. İstisnalar yazılımınızdaki beklenmedik hataları bildirir ve işlemi tamamen sonlandırmanıza olanak verir. Ayrıca geçersiz bir girdi parametresinde olağan dışı bir durum yoktur ve bu sadece basit bir doğrulama hatasıdır.

Kendi oluşturduğunuz istisnaları, yürütme yığınının en üst düzeyindeki try-catch bloğunda yakalamak uygun bir yöntem olacaktır. İstisnaların burada log kayıtları tutulabilir ve kullanıcıya bir uyarı mesajı gönderilebilir. Örneğin Web Api için kullanıcıya 500 mesajı verilebilir.

Özel istisna türleri (custom exceptions) ve harici kütüphaneler

Yazdığınız kodu sizin dışınızda başka geliştiriciler de kullanacaksa istisnai durumlar için farklı bir yol izlenebilir.

Yazdığınız kütüphanede ortaya çıkan beklenmedik istisnai durumları nasıl ele alacağınızı siz bilmesenizde kütüphenenizi kullanan geliştiriciler bunu biliyor olabilir. Örneğin bir FTP client uygulaması kuruyorsunuz ve ana makine client uygulamaya cevap vermiyorsa bir çok kez deneme yapabilirsiniz. Bu durumda client uygulama kendi içerisinde sürekli yeniden bağlanmayı denerse işin içinde çıkılamaz. Bunun yerine kütüphanenizi kullanan geliştiriciye bağlantı hatası fırlatıldığında geliştirici bu durumda ne yapacağını kestirebilir. Sonuçta bağlantı sağlanamaması durumu ile nasıl başa çıkacağını kütüphane kullanıcısı bilir. Bu gibi durumlara karşı her kullanıcının alacağı önlem farklı olabileceğinden bu istisnai durumun özel bir tür olarak diğer istisnai durumlardan ayrılması gerekir. İşte özel istisna türlerinin yürütüleceği yer burasıdır. Oluşturulan özel istisnalar, kütüphane kullanıcılarının karşılaşabileceği her olası soruna karşı doğru tepki vermelerine yardımcı olur.

Özet

  • Özel istisna oluştururken SystemException ve ApplicationException gibi ketegori ayrımları kullanmak yerine Exception temel sınıfından yeni istisna sınıfları üretmek daha uygundur.
  • Eğer yazdığınız kütüphaneyi sizden başka kullanacak olan yoksa, özel istisna türleri oluşturmayın. Mevcut istisna sınıflarını kullanabilirsiniz. Ya da size özel tek bir  istisna türü oluşturarak her yerde kullanabilirsiniz.
  • Eğer bir kütüphane veya framework geliştiriyorsanız ilgili kütüphaneye özel istisna türleri oluşturun. Bu sayede kütüphane geliştiricisi olarak önceden kestiremeyeceğiniz durumlara karşı kullanıcılara bir emniyet önlemi sunmuş olursunuz.

Kaynak: http://enterprisecraftsmanship.com/2016/12/08/custom-exception-types/