C# – Delegasyon İle Exception Yönetimi

Merhaba,

Proje geliştirme süreçlerinde en önemli noktalardan birisi uygulamanın herhangi bir noktasında olan anlık hataları/aksaklıkları/istisnaları takip etmemizi sağlayan try – catch yapılanmasıdır. Bu yapılanma, bir vücudun genel sinir ağ yapılanmasını oluşturan ve sanki deri üzerinde oluşan normal dışı bir müdahalede direkt olarak beyne sinyal gönderen glia hücreleri misali uygulama üzerinde de metot metot işlenmeli ve olası tüm olağan dışı durumda biz developerlara bilgi vermeli ve bir yandan da kullanıcıyı doğru yönlendirmelidir.

Tüm bunların yanında try – catch yapılanması oldukça dikkatli ve doğru hesaplanmış bir şekilde kullanılmalıdır. Bunun nedeni kod akışını reaktif bir takip ile hata verip vermeyeceğini denetleyen ve varsa hata bunu manipüle edebilen bir mekanizma olmasından kaynaklanmaktadır. Dolayısıyla bu bahsedilen yetenekler anlayacağınız yüksek yakıt gerektirmekte ve nihayetinde bu durumda ortaya bir maliyet serilmektedir. Bundan dolayı, hata kontrol mekanizmalarının yersiz kullanımlarının uygulamayı ne kadar olumsuz etkileyeceği hesaba katılmalı ve ona göre yerli ve doğru bir şekilde kullanılması için özen gösterilmelidir.

Gelişmiş uygulama tasarımlarında nesne yönelimli programlama nimetlerini kullanarak İstisnai Durum Yönetimi(Exception Handling) başlığı altında değerlendirilen tasarımlar sayesinde projeye özgü a’dan z’ye özelleştirilmiş stratejiler geliştirilebilir ve tüm bu desenler try – catch yapılanması ile yönetilerek, sistem yöneticisine yahut developera uygulamanın işleyişine dair kritik bilgiler verilebilir. Anlayacağınız üzerine kafa yorulduğu taktirde ucu bucağı olmayan bir derya olabilecek konu üzerinde kalem oynatıyoruz diyebiliriz.

Bizler bu içeriğimizde hata kontrol mekanizmalarını nesne yönelimli programlama prensipleri ışığında ortaya konmuş tasarımlar açısından ele almaktan ziyade direkt olarak yapısal ele alacak ve try – catch yapılanmasını daha aktif ve daha pratik nasıl kullanılabileceğine odaklanacağız. Bunun için yazımızın ileriki satırlarında da görebileceğiniz üzere delegeleri kullanacağız.

Herşeyden önce o bildiğiniz kavisli kıvrımlarıyla hemen hemen her metot içerisinde kullanılan try – catch yapısını şöyle bi alıcı gözüyle tekrar ele alalım 🙂

            try
            {
                //Olası hata verebilecek komutlar...
            }
            catch (Exception)
            {
                //Hata alındığı taktirde çalıştırılacak komutlar...
                throw;
            }
            finally
            {
                //Hata olsa da olmasa da çalıştırılacak komutlar...
            }

Yapısal olarak try – catch blokları, çalışma mantığı açısından oldukça kolay lakin kod israfı açısından ise oldukça maliyetlidir. Nihayetinde tek satırlık işlemlerinde scopelarının kaldırılmasına bile müsade etmeyen bu blok bir kod sayfasında birkaç kez kullanıldığı taktirde kendisini belli etmekte ve kodu haddinden fazla şişirmektedir. Şöyle ki;

        static double Bol(double sayi1, double sayi2)
        {
            try
            {
                double sonuc = sayi1 / sayi2;
                return sonuc;
            }
            catch (DivideByZeroException ex)
            {
                Console.WriteLine("Lütfen sayi2 değerini sıfırdan farklı giriniz.");
                throw;
            }
            finally
            {
                Console.WriteLine("İşlem sonlanmıştır.");
            }
        }

        static double Donustur(string deger)
        {
            try
            {
                double sayisalDeger = double.Parse(deger);
                return sayisalDeger;
            }
            catch (Exception)
            {
                Console.WriteLine("Lütfen degeri sayısal belirtiniz.");
                throw;
            }
        }

görüldüğü üzere yukarıdaki kod esasında ufak iki adet işlem barındırıyor olsada yazılan kodun maliyetine bakınız. Halbuki bu kodun sade hali aşağıdakinden farksızdır 🙂

        static double Bol(double sayi1, double sayi2)
        {
            double sonuc = sayi1 / sayi2;
            return sonuc;
        }

        static double Donustur(string deger)
        {
            double sayisalDeger = double.Parse(deger);
            return sayisalDeger;
        }

Bizler tabi ki de kullanıcıdan gelen opsiyonel değerler doğrultusunda olası hata barındıran tüm kodlarımızı try – catch ile korumaya alacağız. Lakin bu iş bu kadar maliyetli olmak zorunda değildir. Bunun için yukarıdaki satırlarda da bahsedildiği üzere delegasyonel bir yaklaşımla daha pratik ve kullanışlı bir şekilde tasarım oluşturularak, çözülebilir.

Peki exception yönetimi için delegasyondan kastınız nedir?
Bu soruya cevap olarak “System” namespace’i altında bulunan Action delegate’ini verebiliriz. “Action” özü itibariyle bir delegate olduğu için imzası gereği geriye değer döndürmeyen herhangi bir metodu temsil edebilmektedir. Eğer ki, geriye değer döndüren metotları temsil eden bir delegasyonla çalışmak istiyorsanız ya custom bir delegete geliştirmeniz yahut önceden tanımlanmış bir delege türü olan Func yapılarına göz atmanız gerekmektedir.

Delegasyondan kastınızın Action delegesi olduğunu anladım. Peki bu delegasyon ile nasıl bir tasarım gerçekleştirmemizi öneriyorsunuz?
Bu soruyu teoride cevaplarsak eğer; tüm uygulamada exception kontrol sorumluluğunu üstlenecek olan bir metot tasarlanır ve bu metotta “Action” tipinde bir parametre alınır. Bu parametre dışarıdan verilen callback fonksiyon içerisindeki işlemleri ilgili metot içerisinde önceden varsayılan olarak yazılmış bir try – catch yapılanması içerisinde uygun bir yerde invoke edecektir. Dolayısıyla bir metot ve o metoda verilen callback fonksiyon aracılığıyla yazılan kod, esasında try – catch bloğu içerisinde çalıştırılacağı için kod maliyetini oldukça minimize etmiş ve mümkün mertebe temiz kod yazabilme olanağı elde etmiş olunacaktır.

Yine ilgili soruyu pratikte cevaplarsak eğer; exception kontrol sorumluluğunu üstlenecek olan metodumuzu aşağıdaki gibi geliştirmemiz gerekmektedir.

        static void ExceptionHandler(Action metot)
        {
            try
            {
                metot();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {

            }
        }

Görüldüğü üzere “Action” delegasyon tipinden olan “metot” isimli parametre try bloğu içerisinde invoke edilmektedir. Bu metot ilgili delegasyon içerisinde olan tüm operasyonları invoke edildiği noktada tetikleyeceğinden ve o noktanında try – catch bloğu içerisinde olmasından dolayı doğal bir hata kontrol mekanizması söz konusu olacaktır. Bu yapıyı aşağıdaki gibi kullanarak yukarılardaki satırlarda verilen örneklere nazaran kodumuzu geliştirirsek eğer aradaki farkı görmenizi istirham ederim.

        static double Bol(double sayi1, double sayi2)
        {
            double sonuc = 0;
            ExceptionHandler(() => sonuc = sayi1 / sayi2);
            return sonuc;
        }

        static double Donustur(string deger)
        {
            double sayisalDeger = 0;
            ExceptionHandler(() => sayisalDeger = double.Parse(deger));
            return sayisalDeger;
        }

Nihai olarak, her daim kodun genel anatomisini destekleyen çalışmaların sade ve sadece mimarisel tasarımlarla değil azda olsa bu şekilde prosedürel yaklaşımlar sergileyerekte sağlanabildiğinin farkında olmanız ve ilgilenenlerin faydalanması dileğiyle 🙂

Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar dilerim…

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*