Serdar YILMAZ / Software Developer
Bizimkisi Bir “Bug” Hikayesi...
Akbank Bankacılık Merkezi

Arşiv 'Programlama' Kategori

C# – Katmanlı Mimaride Generic’ler

.net
Generic sınıfların, metotların ve arayüzlerin nasıl oluşturulduğundan (bkz: Generic Sınıflar, Metotlar ve Arayüzler) ve ne tür kısıtlar eklenebileceğinden (bkz: Generic Kısıtlar) bir önceki yazılarımızda bahsetmiştik. Bu içerikte ise Generic’lerin gerçek bir projede ne amaçla ve nasıl kullanılabileceğini olabildiğince yalın ve anlaşılır bir şekilde aktarmaya çalışacağım.

Örnek Senaryo: Müşteri, Ürün ve Kategori bilgilerini tutan Customer, Product ve Category isimli sınıflarımız olduğunu farz edelim. Müşterileri, Ürünleri ve Kategorileri listeleyecek, arama ve silme yapabilecek Manager sınıfları, Generic’leri kullanarak oluşturmaya çalışalım.

Müşterinin Id, isim ve adres bilgilerini tutan Customer isimli bir sınıfımız olsun.

Müşterileri listeleyecek, silecek ve müşteri listesinde arama yapabilecek metotlara ihtiyacımız olduğunu farz edelim. Bu ihtiyaçları ICustomerManager arayüzünde bildiriyoruz.

ICustomerManager arayüzünde bildirimini yapmış olduğumuz metotları, CustomerManager isimli sınıf içerisinde tanımlıyoruz. CustomerManager sınıfında arama, silme ve listeleme işlemlerini _customers koleksiyonu üzerinde yapmaktayım, sizler geliştireceğiniz uygulamalarda bu işlemleri veritabanı üzerinde yapabilirsiniz.

Şimdi ise Ürün ve Kategori bilgilerini tutan Product ve Category sınıflarına ihtiyacımız olduğunu düşünelim.

Müşterilerde olduğu gibi Kategorilerde ve Ürünlerde de listeleme, silme ve arama işlemlerini yapacak metotlara ihtiyacımız olacaktır. Bu ihtiyaçları IProductManager ve ICategoryManager arayüzlerinde bildiriyoruz.

Oluşturmuş olduğumuz arayüzleri inceleyecek olursak; her üç arayüzde de(ICustomerManager, IProductManager, ICategoryManager) List, Find ve Remove metotlarının bildiriminin yapıldığını ve sadece parametre türlerinin ve metotların geri dönüş türlerinin farklılık gösterdiğini görebiliriz. O halde Generic bir arayüz oluşturup, parametre türlerini ve metotların geri dönüş türlerini parametrik hale getirebiliriz.

Generic IRepository<T> arayüzü sayesinde, Manager sınıflar (CustomerManager, ProductManager, CategoryManager) için oluşturduğumuz arayüzlerin (ICustomerManager, IProductManager, ICategoryManager) hepsinde aynı metotların bildirimini yapmak zorunda kalmayacağız. Artık tüm Manager sınıflarda bulunması gereken ortak metotların bildirimini IRepository<T> arayüzünde yapacağız. Manager sınıfa özgü olan metotların bildirimini de o Manager sınıf için oluşturduğumuz arayüzün içerisinde yapacağız.

Kod kalabalığını arttırmamak için yukarıda ki kod bloğunda Product ve ProductManager sınıflarına yer vermedim. Yukarıdaki kod bloklarını inceledikten sonra aklınıza şöyle bir soru gelebilir; ICustomerManager ve ICategoryManager arayüzlerinin içerisinde ekstradan herhangi bir metot bildirimi yapılmadı, o halde neden tanımlama gereği duyuldu, neden IRepository arayüzü direkt Manager sınıflara implement edilmedi ?

IRepository<T> arayüzü içerisinde tüm Manager sınıflarda bulunması gereken metotların bildirimi yapılır. Ancak Manager sınıfların içerisinde IRepository<T> arayüzünde bildirilmiş metotların haricinde kendilerine has metotlar da bulunabilir. Örneğin CustomerManager sınıfı içerisinde müşterilerin adres bilgisini döndüren bir metot olabilir. Eğer IRepository<T> arayüzü içerisinde bu metodun bildirimini yapmış olsaydık, bu metodu CategoryManager içerisinde de gerek olmamasına rağmen tanımlamamız gerekirdi. Bu yüzden tüm Manager sınıflar için ortak olan metotlar IRepository<T> arayüzünde bildirilir, Manager sınıfa özel olan metotlarda o Manager sınıfa özel olarak oluşturulmuş arayüzde bildirilir.

Son olarak; dikkat edecek olursak, IRepository<T> arayüzüne herhangi bir kısıt konulmamış durumda. Bu yüzden arayüze veri tipi olarak hem değer tipliler hem de referans tipliler gönderilebilir. Ancak biz arayüze sadece Customer ve Category gibi Entity’lerin gönderilmesini istiyoruz. O halde IRepository<T> arayüzüne bir kısıt eklememiz gerekiyor.

IEntity adında bir arayüz oluşturduk ve where T : class, IEntity kısıtı ile IRepository<T> arayüzüne sadece IEntity arayüzü implement alan referans tiplilerin gönderilmesine izin verdik.  IRepository<T> arayüzüne Customer ve Category sınıflarının gönderilmesini istediğimizden IEntity arayüzünü bu sınıflara implement ediyoruz. Kod bloklarının son hali;

Serdar YILMAZ

C# – Generic Kısıtlar

.net

Bir önceki yazımızda(*) Generic sınıfların, metotların ve arayüzlerin nasıl oluşturulduğundan bahsettik. Bu yazımızda ise Generic sınıflara, metotlara ve arayüzlere ne tür kısıtlamalar getirebileceğimizden bahsedeceğiz.

Değer ve Referans Tip Kısıtı

ExampleClass Generic sınıfına herhangi bir kısıt uygulanmadığı için T yerine int, double, float gibi değer tipleri gönderebileceği gibi; string, object, array gibi referans tipleri de gönderilebilir.

Değer Tipleri: “int”, “long”, “float”, “double”, “decimal”, “char”, “bool”, “byte”, “short”, “struct”, “enum”
Referans Tipleri: “string”, “object”, “class”, “interface”, “array”, “delegate”, “pointer”

Değer ve referans tipler hakkında daha detaylı bilgi edinmek için Değer ve Referans Parametreleri başlıklı içeriği okuyabilirsiniz.

Oluşturduğumuz ExampleClass Generic sınıfına sadece referans tip veya sadece değer tip gönderilmesini isteyebiliriz. Böylesi bir durumda Generic sınıfımıza bir kısıt koymamız gerekmekte.

Sadece referans tip gönderilmesi için;  

ExampleClass Generic sınıfına, “where T:class” kısıtı sayesinde sadece referans tipleri gönderilebilir. Değer tipli bir tür gönderildiği takdirde hata oluşacak ve proje derlenmeyecektir.

Sadece değer tip gönderilmesi için; 

ExampleClass Generic sınıfına, “where T : struct” kısıtı sayesinde sadece değer tipleri gönderilebilir. Referans tipli bir tür gönderildiği takdirde hata oluşacak ve proje derlenmeyecektir.

new() Kısıtı

Generic sınıfa gönderilen veri tipini, sınıf içerisinde new’liyorsak, yani o tipten yeni bir nesne oluşturuyorsak; Generic sınıfa “new() kısıtını uygulamamız gerekmektedir. Eğer Visual Studio ile geliştirme yapıyorsanız zaten IDE sizi new() kısıtını uygulamaya zorlayacaktır.

ExampleClass Generic sınıfında gönderilen veri tipi createObject metodunda new’lenerek yeni bir nesne oluşturulmak istendiğinden new() kısıtı uygulanmıştır. Artık ExampleClass Generic sınıfına sadece nesne oluşturulabilen yani new() lenebilen türler gönderilebilir.

Arayüz kısıtı

Generic sınıfa sadece belirttiğimiz arayüzleri implement alan türlerin gönderilmesini istiyorsak arayüz kısıtını uygularız.

IExample1 ve IExample2 isminde iki arayüz oluşturduk. IExample1 arayüzünü ExampleClass1 ve ExampleClass2‘ye implement ettik. IExample2 arayüzünü de ExampleClass3 sınıfına implement ettik.

Generic sınıfımıza “where T : IExample1” kısıtını eklediğimiz takdirde; Generic sınıfımıza veri türü olarak sadece IExample1 arayüzünü implement alan ExampleClass1 ve ExampleClass2 gönderilebilecektir. ExampleClass3 sınıfı IExample1 arayüzünü implement almadığı için GenericClass’a veri tipi olarak gönderilmek istendiğinde hata alınacaktır. Arayüz kısıtı sayesinde Generic sınıflara sadece belli arayüzleri implement alan sınıfların gönderilmesini sağlayabiliriz.

Birden Fazla Kısıt Ekleme

Yukarıda anlatmış olduğumuz kısıtları birlikte de kullanabiliriz.

ExampleClass Generic sınıfına sadece referans tipli olan, IExample1 arayüzünü implement alan ve new’lenebilen veri türleri gönderilebilir.

new() kısıtı her zaman en son da yer almalıdır.

Generic Metotlara Kısıtların Eklenmesi

Generic kısıtları tıpkı sınıflarda kullandığımız gibi generic metotlarda da kullanabiliriz.

createObject Generic metoduna sadece referans tipli olan, IExample1 arayüzünü implement alan ve new’lenebilen veri türleri gönderilebilir.

Serdar YILMAZ

C# – Generic Sınıflar, Metotlar ve Arayüzler

.net
Bu içerikte Generic’leri anlatırken, konuya aşinalığı sağlamak adına basit örnekler üzerinden, özellikle yazım kurallarını ön planda tutarak içeriği oluşturacağım. Sonradan yayınlayacağım içeriklerde ise Generic’lere daha detaylı bir şekilde değinip, gerçek hayatta kullanımına dair örneklere yer vereceğim.

Generic Sınıflar 

Şu ana kadar oluşturduğumuz sınıflarda, sınıf içerisindeki değişkenlerin, metotların ve parametrelerin veri tiplerini onları tanımlarken belirttik.

Example sınıfında example_1 değişkeninin veri tipini ve example_2 metodunun parametresinin veri tipini de int olarak belirttik. Artık example_1 değişkenine int haricinde bir değer atamamız veya example_2 metoduna int’den farklı bir parametre göndermemiz mümkün değildir.

Generic yapıyı kullandığımız takdirde sınıf içerisindeki değişkenlerin, parametrelerin ve metotların geri dönüş tiplerini o sınıftan bir nesne oluştururken belirleyebilmekteyiz.

Example sınıfından nesne oluştururken T (T yerine farklı bir isim verilebilir, isteğe bağlı) yerine hangi veri tipini yazarsak; sınıf içerisindeki değişken ve parametrenin tipi o şekilde olacaktır.

exp_1 nesnesinde example_1 değişkeni ve example_2 metodunun parametresi int tipinde olacaktır. exp_2 nesnesinde ise example_1 değişkeni ve example_2 metodunun parametresi string tipinde olacaktır. Yani Generic’ler sayesinde bir sınıfın elemanlarının veri tiplerini ihtiyaç doğrultusunda yeni bir sınıf tanımlamaya gerek kalmadan değiştirebilmekteyiz.

generic c#

Generic Metotlar

Bazen bir sınıf içerisindeki metotların sadece birkaçını Generic olarak kullanmak isteyebiliriz. Böylesi bir durumda sınıfı Generic yapmak yerine sadece ilgili metotları Generic yapmak çok daha mantıklı olacaktır.

example_1 ve example_2 metotlarının parametre ve geri dönüş tipleri bellidir, bu yüzden farklı türden bir değer almaları veya geriye döndürmeleri mümkün değildir. Ancak example_3 metodu Generic olarak tanımlandığı için parametre ve geri dönüş tipi çağrılmadan önce belirtilmelidir. example_4 metodunun ise geri dönüş tipi string olup, parametresinin veri tipi çağrılmadan önce belirtilmelidir.

Ekran Çıktısı:

Generic Arayüzler

Tıpkı sınıflarda olduğu gibi arayüzleri de Generic olarak tanımlayabiliriz.

Tek fark; Generic sınıflarda veri tipini o sınıftan bir nesne oluştururken belirtmekteyiz, Generic arayüzlerde ise veri tipini, o arayüzü bir sınıfa implement ederken belirtmekteyiz.

Arayüzlerde Generic Metot Bildirimi 

Arayüz içerisindeki metotların tamamı Generic değilse; Arayüz yerine sadece ilgili metotları Generic yapmak daha mantıklı olacaktır, tıpkı Generic metotlarda anlattığımız gibi.

IExample arayüzü Generic olmadığı için herhangi veri tipi belirtmeden ExampleClass_1 sınıfına implement edebildik. example_2 metodu Generic olduğu için veri tipi metot çağrılırken girilecektir.

Statik Generic Sınıflar

Statik sınıflar ile statik olmayan sınıfların Generic yapılması noktasında arada herhangi bir fark bulunmuyor. Statik sınıfların metot ve değişkenlerine nesne oluşturmadan erişebildiğimiz için, veri tipini sınıf adını yazdıktan hemen sonra belirtiyoruz.

Generic’lerde Birden Fazla Veri Tipinin Kullanılması

Generic ifadelere birden fazla veri tipi gönderebiliriz.

Serdar YILMAZ

C# – Merkezi İstisnai Durum Yönetimi

.netTry-Catch-Finally blokları ile çalışma anında meydana gelen hataları nasıl yakalayabileceğimizi ve türlerine göre nasıl filtreleyebileceğimizi öğrendik. Hataların türlerine göre filtrelenmesi işleminde bazen Catch bloklarının sayısı bir hayli fazla olabilmekte.

ExampleClass sınıfının içerisinde tanımlanmış olan ExampleMethod_1 ve ExampleMethod_2 metotlarını inceleyecek olursak; Catch bloklarından dolayı metotların gövdesinde kod kalabalığı oluştuğunu görebiliriz. Üstelik henüz metodun görevini ve Catch bloklarının içeriğini yazmadık bile! ExampleMethod_1 ve ExampleMethod_2 gibi içerinde bir çok Catch bloğu barındıran, onlarca metodun oluşturduğu bir sınıftaki kod kalabalığını tahmin edebilirsiniz.

Tüm metotlarda Try-Catch-Finally bloklarını tekrar tekrar yazmak yerine, oluşan hataları yakalayan ve türlerine göre filtreleyen merkezi bir İstisnai Durum Yönetimi metodu oluşturarak, kod kalabalığını ciddi anlamda azaltabiliriz. Bunun için öncelikle yukarıdaki ExampleClass sınıfımızı Try-Catch bloklarından arındırıp, geriye sadece metotların görevlerini yani metotlar çağrıldığında çalışmasını istediğimiz kodları bırakıyoruz.

ExampleClass sınıfındaki Try-Catch bloklarını kaldırarak kod kalabalığını ciddi anlamda azalttık. Şimdi, ExampleMethod_1 ve ExampleMethod_2 metotlarının çalışması esnasında ortaya çıkan hataları yakalayan ve türlerine göre filtreleyen metodumuzu yazıyoruz.

Oluşturduğumuz ExceptionCatcher metodunu dikkatlice inceleyecek olursak; Action türünde bir parametre aldığını görebiliriz. Bu da şu anlama geliyor; biz ExceptionCatcher metoduna parametre olarak bir metot veya kod bloğu gönderebiliriz (Delegate’leri anlatacağım içeriklerde bu konuya daha detaylı bir şekilde değineceğim).

ExceptionCatcher metoduna parametre olarak gönderdiğimiz metotları Invoke() ile çağırmaktayız. Yani toparlayacak olursak; ExceptionCatcher metoduna parametre olarak ExampleMethod_1 ve ExampleMethod_2 metotlarını göndereceğiz ve Invoke() metodu ile bu metotları Try bloğu içerisinde çalıştırıp, hata oluşması durumunda da Catch blokları ile filtreleyeceğiz.

Son olarak ExceptionCatcher metoduna parametre olarak bir metot göndermek yerine, bir kod bloğu göndereceğimiz farklı bir örnek yapalım.

Yukarıdaki uygulamamızda ExceptionCatcher metoduna parametre olarak bölme işlemini gerçekleştiren kod bloğunu gönderdik. Göndermiş olduğumuz kod bloğu ExceptionCatcher metodunun Try bloğunda çalışacaktır. Klavyeden sayı yerine harf veya karakter girildiğinde veya sayi_2 değişkenine sıfır değeri atandığında oluşacak hatalar ilgili Catch bloğu tarafından yakalanacaktır.

Serdar YILMAZ

C# – Exception Sınıfı Oluşturma

.netİstisnai Durum Yönetimi başlıklı yazımızda Try-Catch-Finaly blokları ile uygulamamızda meydana gelen hataları nasıl yakalayacağımıza ve türlerine göre nasıl filtreleyebileceğimize değinmiştik. Bu yazımızda ise; kendi Exception sınıflarımızı nasıl oluşturacağımıza ve hangi amaçlar doğrultusunda kullanabileceğimize değineceğiz.

Hazır Exception Sınıfları

.NET Framework içerisinde bir çok hazır Exception sınıfı bulunmaktadır. Uygulamamızda çalışma anında bir hata meydana geldiğinde .Net,  hatanın türüne göre ilgili Exception sınıfından bir nesne oluşturarak geriye fırlatmaktadır.

Yukarıdaki örneği inceleyecek olursak; Bir sayının sıfıra bölümü sonsuz olduğundan, sayi_1 değişkeni sayi_2 değişkenine bölünmek istendiğinde .Net bir Exception nesnesi fırlatacaktır. Bize düşen ise try-catch blokları ile bu hatayı yakalamaktır. Yukarıdaki uygulama çalıştırıldığı takdirde ekran çıktısı aşağıdaki gibi olacaktır;

Ekran çıktısını inceleyecek olursak; .Net’in sadece hata vermekle kalmadığını, hatanın neden oluştuğuna dair bilgide verdiğini görebiliriz. Tıpkı bu örnekte olduğu gibi, bizlerde hatalı işlem yapılmasını önlemek amacıyla kontrollü bir şekilde Exception’lar fırlatabiliriz.

Kendi Exception Sınıflarımızı Nasıl Oluşturabiliriz ?

Exception sınıflarının nasıl oluşturulduğunu ve kullanıldığını örnek bir senaryo üzerinden ilerleyerek anlamaya çalışalım. Kullanıcıların şifrelerini değiştirebileceği bir metot yazmak istediğimizde, metot içerisinde en basit haliyle aşağıdaki kontrolleri yapabiliriz;

  1. Belirtilen kullanıcı adı sistemimizde var mı? Eğer yoksa olmayan bir kullanıcının şifresi değiştirilmek isteniyor demektir, böylesi bir durumda bir hata mesajı fırlatmamız gerekir.
  2. Şifresini değiştirmek isteyen kullanıcıdan, doğrulama amaçlı eski şifresini girmesini istediğimizde yanlış bir şifre girerse bir hata mesajı döndürmemiz gerekir.

Yukarıdaki iki durumu da dikkate alarak Exception sınıflarımızı oluşturalım;

Kullanıcı sistemimize kayıtlı olmadığı durumda fırlatacağımız UserNotFoundException ve yanlış şifre girildiği zaman fırlatacağımız WrongPasswordException sınıflarımızı oluşturduk. Dikkat edilecek olursa her iki sınıfımızda Exception sınıfından türetilmiştir ve base anahtar sözcüğü ile hata mesajları temel sınıf olan Exception sınıfının yapıcı metoduna parametre olarak gönderilmiştir.

Oluşturacağımız Exception sınıfları, “Exception” sınıfından türetilmelidir.

base anahtar sözcüğünün kullanımı hakkında daha detaylı bilgi edinmek için Yapıcı Metotlar ve Kalıtım başlıklı içeriği okuyabilirsiniz.

Nasıl Exception Fırlatabiliriz ? 

Exception sınıflarımızı oluşturduğumuza göre şimdi onları hatalı işlem yapılmasını önlemek amacıyla kontrollü bir şekilde fırlatabiliriz. Örnek senaryo üzerinden giderek sınıfımızı ve metodumuzu oluşturalım;

UserManager isimli class içerisinde kod kalabalığını arttırmamak için; kullanıcı adı ve şifre çiftlerini tutan bir Dictionary tanımlayıp, kullanıcının kayıt olup-olmadığını, şifrenin doğru girilip-girilmediğini bu koleksiyon sınıfı üzerinden kontrol ettik. Sizler bu işlemleri veritabanı üzerinden yapabilirsiniz. Dictionary hakkında daha detaylı bilgi edinmek için Dictionary Sınıfı başlıklı içeriği okuyabilirsiniz.

PasswordChange() isimli metodumuzu açıklayacak olursak; İlk olarak parametre olarak gelen kullanıcı adının _Users koleksiyonunda olup olmadığını kontrol ediliyor ve eğer kullanıcı mevcut değilse throw anahtar sözcüğü ile UserNotFoundException istisnası fırlatılıyor. Kullanıcı mevcut ise parametre olarak gelen şifre bilgisinin doğru olup olmadığı kontrol ediliyor ve şifre bilgisi yanlış ise WrongPasswordException istisnası fırlatılıyor. Doğru girildiği takdirde de ilgili kullanıcının şifre bilgisini güncelleniyor.

throw anahtar sözcüğü ile oluşturduğumuz exception sınıflarını nasıl fırlatacağımızı öğrendik, şimdi sıra onları yakalamakta.

_Users koleksiyonunda “serdaryilmaz” adında bir kullanıcı olmadığı için UserNotFoundException istisnasını fırlatacaktır. Ekran çıktısı;

“srdrylmz” kullanıcısının şifresi yanlış girildiğinden WrongPasswordException istisnasını fırlatacaktır.

Serdar YILMAZ

C# – İstisnai Durum Yönetimi

.netUygulamalarımızı geliştirirken bir syntax hatası (Kod satırlarının sonuna noktalı virgül koymayı unutmak gibi) yaptığımızda, Visual Studio gerekli uyarıyı vererek projeyi derlememizi engelleyecektir. Ancak çalışma anında ortaya çıkabilecek hataları Visual Studio’nun önceden tespit edebilmesi mümkün değildir. Bu yüzden çalışma anında ortaya çıkabilecek hataların uygulamamızı çökertmesine izin vermemek için Try-Catch-Finally ile istisnai durum yönetimine başvururuz.

Yukarıdaki uygulama Visual Studio tarafından sorunsuz bir şekilde derlenecektir ve doğru değerler girildiği sürece program sorunsuz bir şekilde çalışacaktır.  Ancak programı kullanan kullanıcı klavyeden sayı yerine harf veya karakter girdiğinde veya sayi_2 değişkenine sıfır değerini atadığında (bir sayının sıfıra bölümü sonsuzdur) uygulamamız çökecektir.

Çalışma anında ortaya çıkan hataların uygulamamızı çökertmemesi için Try-Catch-Finally bloklarını kullanırız.

Try-Catch-Finally Blokları

Try Bloğu: Çalışma anında hata çıkarma olasılığı olan kodlarımızı Try bloğu içerisine yazarız. Eğer Try bloğu içerisine yazılmış olan kodlarda bir hata meydana gelirse, oluşan hata bir Exception nesnesi olarak catch bloğuna gönderilir.

Catch Bloğu: Try bloğu içerisine yazılmış olan kodlarda bir hata meydana geldiği an, program Try bloğundan çıkarak Catch bloğuna girer. Uygulamamız çalışırken hata oluşması durumunda, uygulanmasını istediğimiz çözüm senaryosunu Catch bloğu içerisine yazarız.

Finally Bloğu: Try bloğu içerisinde bir hata meydana gelmediği sürece Catch bloğu içerisindeki kodlar çalışmaz. Ancak Finally Bloğu hata meydana gelse de, gelmese de her halükarda çalışır. Finally opsiyonel bir bloktur, istenilmediği taktirde yazılmayabilir.

Artık sayı yerine harf girildiğinde veya sayi_2 değişkenine sıfır değeri atandığında uygulamamız çökmeyecektir. Hata meydana geldiği an arkaplanda bir Exception nesnesi oluşturulup Catch bloguna parametre olarak gönderilecektir. Bu parametre aracılığıyla oluşan hata hakkında bilgi alınabilir.


Yukarıdaki örnekte Exception’da ki hata mesajı kullanıcıya gösterilmek üzere ekrana yazdırıldı ancak profesyonelce geliştirilen projelerde Exception’da ki hata mesajı doğrudan kullanıcıya sunulmaz. Bunun başlıca iki sebebi bulunmaktadır.

  1. Exception’da ki hata mesajı genellikle kullanıcıların anlayamayacağı teknik terimleri içerir.
  2. Exception’da ki hata mesajı uygulamamız hakkında bilinmemesi gereken bilgileri içeriyor olabilir.

Bu yüzden Exception’da ki hata mesajını genellikle loglamak amacıyla kullanırız.

Oluşan Hataları Filtreleme

Try bloğu içerisine yazdığımız kodlarda farklı türden hatalar meydana gelebilir. Eğer her hata türü için farklı bir çözüm senaryomuz varsa, oluşan hataları filtrelememiz gerekir. Yukarıdaki örneğimizde iki farklı hata söz konusuydu; ilki klavyeden sayı yerine harf veya karakter girildiğinde ortaya çıkarken, ikincisi sayi_2 değişkenine sıfır değeri atandığında ortaya çıkmaktaydı. Oluşan hataları filtreleyecek şekilde uygulamamızı yeniden yazacak olursak;

“FormatException” ve “DivideByZeroException” gibi bir çok hazır Exception sınıfı bulunmakta. Try bloğunda hata meydana geldiği an arkaplanda bir Exception nesnesi oluşturulur ve Catch blokları yukarıdan aşağıya incelenir, oluşan hata türü hangi Catch bloğuna uyuyorsa o Catch bloğu çalışır. Klavyeden sayı yerine harf veya karakter girildiğinde ilk Catch bloğu çalışacaktır, sayi_2 değişkenine sıfır değeri atandığında da ikinci Catch bloğu çalışacaktır. “FormatException” ve “DivideByZeroException” haricinde bir hata meydana geldiğinde de üçüncü Catch bloğu çalışacaktır.

“catch(Exception exception)” şeklinde tanımlanan Catch bloğu tüm hata türlerini kapsar. O yüzden hata türüne göre filtreleme işlemi yaparken “catch(Exception exception)” bloğunu en sona yazmalıyız.

Eğer “catch (Exception exception)” bloğunu en üste yazarsak; Catch blokları yukarıdan aşağıya doğru incelendiği için ve “catch (Exception exception)” bloğu da tüm hata türlerini kapsadığı için;  “catch (FormatException exception)” ve “catch (DivideByZeroException exception)” blokları hiç bir zaman çalışmayacaktır. Tüm hata türleri “catch (Exception exception)” bloğu tarafından yakalanacaktır.

Serdar YILMAZ

C# – Abstract Sınıflar

.netTamamen kalıtım amaçlı kullanacağımız temel sınıfları oluştururken Abstract anahtar sözcüğünü kullanırız. Abstract bir sınıf oluşturabilmek için erişim belirtecinden sonra “abstract” anahtar sözcüğünü yazmamız gerekmektedir. Abstract sınıflar içerisinde hem metot tanımlayabilir hem de arayüzler de olduğu gibi metot bildirimi yapabiliriz.

TemelSinif, abstract bir sınıf olduğu için sadece kalıtım amaçlı kullanılabilir. Metot_1(), TemelSinif‘dan türetilen sınıflara doğrudan aktarılacaktır. Bildirimi yapılmış olan Metot_2()‘nin ise türetilmiş sınıflar içerisinde tanımlanması (metot gövdesinin yazılması) gerekmektedir.

Abstract sınıflarda metot bildirimi yapabilmek için erişim belirtecinden sonra “abstract” anahtar sözcüğünü yazmamız gerekmektedir.

TuretilmisSınıf, TemelSinif‘dan türetildiği için TuretilmisSınıf içerisinde Metot_2()‘nin tanımlamasını yapmamız gerekmektedir. Bu örnekten de anlaşılacağı üzere abstract sınıfları arayüzler (interface) gibi kullanabiliriz.

Abstract sınıf içerisinde bildirimi yapılmış olan metotları (Bkz: Metot_2()) türetilmiş sınıflar içerisinde tanımlayabilmemiz için override anahtar sözcüğünü kullanmamız gerekmektedir.

Ekran çıktısı:

Abstract Sınıfların Normal Sınıflardan Farkı Nedir?

Fark 1: Normal sınıflar içerisinde metot bildirimi yapılamazken, Abstract sınıflar içerisinde tıpkı arayüzler de olduğu gibi metot bildirimi yapılabilir. Bildirimi yapılan metotlar, Abstract sınıftan türeyen sınıflar içerisinde tanımlanmak zorundadır.

Fark 2: Normal sınıflardan “new()” anahtar sözcüğü ile nesneler oluşturulabilir ancak Abstract sınıflar tamamen kalıtım amaçlı geliştirildiğinden Abstract sınıflardan nesne oluşturulamaz.

Abstract Sınıfların Arayüzlerden Farkı Nedir?

Fark 1: Arayüzlerde sadece metot bildirimi yapılabilirken Abstract sınıflarda hem metot bildirimi yapılabilir hem de metot tanımlanabilir.

Fark 2: Bir sınıfa sadece bir tane Abstract sınıf inherit edilebilir ancak aynı sınıfa birden fazla arayüz implement edilebilir.

TuretilmisSinif‘a Abstract sınıflardan sadece bir tanesini inherit edebiliriz. Yani TuretilmisSinif‘a hem AbstractSinif_1‘i hem de AbstractSinif_2‘yi inherit edemeyiz.

Ancak bir Abstract sınıfa, başka bir Abstract sınıfı inherit ederek bu kısıtı kaldırmak mümkün.

AbstractSinif_1‘e AbstractSinif_2 inherit edildiği için, AbstractSinif_1‘den türetilen bir sınıf hem AbstractSinif_1 içerisinde bildirimi yapılmış olan metodu (Metot_1()) hem de AbstractSinif_2 içerisinde bildirimi yapılmış olan metodu (Metot_2()) içermek zorundadır.

Abstract Sınıflara Hangi Durumlar da İhtiyaç Duyarız

Yılın her bir ayı için bir sınıf oluşturmamız gerektiğini düşünelim ve bu sınıflar içerisinde ilgili ayın kaç günden oluştuğu, yılın kaçıncı ayı olduğu ve her bir gününün kaç saatten oluştuğu bilgilerini döndürecek metotların olmasını istediğimizi varsayalım.

İlk başta aklımıza bir arayüz kullanmak gelebilir. Ay isminde bir arayüz tanımlayıp, içerisinde ihtiyacımız olan metotların bildirimini yaptıktan sonra bu arayüzü aylarımızı temsil eden sınıflara implement ederek bir çözüm geliştirebiliriz.

Ancak ayları temsil eden sınıfları dikkatlice inceleyecek olursak, GunlerKacSaattir() metodunun hepsinde aynı sonucu döndürecek şekilde tanımlandığını görebiliriz. Her bir ay için aynı metodu tekrar tekrar tanımlayıp kod tekrarı yapmak yerine, bir defaya mahsus tanımlamak çok daha verimli olacaktır. Arayüzler içerisinde metot tanımı yapılamayacağından, bu örnekte arayüz yerine abstract sınıf kullanacağız.

İçerisinde hem metot tanımı hem de metot bildirimi yapabileceğimiz bir yapıya ihtiyacımız olduğundan Ay isminden bir abstract sınıf oluşturduk. GunlerKacSaattir() metodu tüm aylar için aynı sonucu döndüreceğinden, bu metodu direkt abstract sınıf içerisinde tanımladık. Böylece 12 ay için aynı metodu 12 defa yazmak yerine, bir defa yazmış olduk.

Eğer bir metot tüm türetilmiş sınıflarda aynı şekilde tanımlanıyorsa o metodu abstract sınıf içerisinde standart bir metot tanımlar gibi tanımlarız.

KacGundenOlusur() metodunu abstract sınıf içerisinde tanımlamak yerine sadece bildirimini yapıp, Ayları temsil eden sınıflarımız içerinde tanımlayabilirdik. Ancak bu metot Ocak, Mart, Mayıs, Temmuz, Ağustos, Ekim ve Aralık ayları için aynı sonucu (31) döndürmesi gerektiğinden, en azından bu aylar için tekrardan KacGundenOlusur() metodunun tanımını yapmamak adına varsayılan olarak geriye 31 döndüren ancak istenilen sınıf içerisinde override edilerek yeniden tanımlanabilecek şekilde kullanılabilmesi için virtual olarak tanımladık (Bkz: C# – Virtual Metotlar).

Eğer bir metot bir çok türetilmiş sınıfta aynı şekilde tanımlanıyor iken sadece bir kaçında değişik şekilde tanımlanıyorsa o metot abstract sınıf içerisinde virtual olarak tanımlanır.

YilinKacinciAyidir() metodu her bir ay için farklı bir değer döndüreceğinden, bu metodun sadece bildirimi yapıyoruz.

Eğer bir metot tüm türetilmiş sınıflarda farklı şekilde tanımlanıyorsa o metodun abstract sınıf içerisinde sadece bildirimi yapılır.

Serdar YILMAZ

C# – Virtual Metotlar

.netKalıtım yolu ile sınıfların birbirinden türetilebileceğini ve bir sınıfın diğer bir sınıftan türediği zaman, türediği sınıfın bütün özelliklerini içereceğini “C# – Kalıtım” başlıklı içeriğimizde açıklamıştık.

Temel sınıftan türetilmiş sınıflara aktarılan metotları her zaman olduğu gibi kullanmak istemeyebiliriz. Bu metotları türetilmiş sınıf içerisinde yeniden tanımlayabilmek için virtual ve override anahtar sözcüklerini kullanırız.

Virtual metotlar kalıtım yolu ile aktarıldıkları sınıfların içerisinde override edilerek değiştirilebilirler. Eğer override edilmezlerse temel sınıf içerisinde tanımlandıkları şekilde çalışırlar.

Kod kalabalığını arttırmamak ve konuyu daha anlaşılır bir şekilde anlatmak için; sınıfları karışıklığa yer vermeyecek şekilde isimlendirip, konumuzun dışında olan kod satırlarına/bloklarına yer vermemeye çalışacağım.

Örnek bir senaryo üzerinden gidecek olursak; User (kullanıcı), Category (kategori) ve Article (Makale) isimli sınıflarımızın olduğunu ve bu sınıfların Database sınıfından türetildiğini düşünelim.

Database sınıfı içerisindeki Insert() metodu kalıtım yolu ile User, Category ve Article sınıflarına aktarılacaktır.

Yukarıdaki konsol uygulamasının ekran çıktısı;

Ancak biz User sınıfında Insert() metodunun kullanıcı adı kontrolü yaptıktan sonra veriyi kaydetmesini istiyor olabiliriz. Temel sınıf içerisindeki bir metodun gövdesini türetilmiş sınıflar içerisinde değiştirebilmek için Temel sınıf içerisindeki metodu virtual olarak tanımlamamız gerekir.

Database sınıfı içerisindeki Insert() metodu virtual olarak belirtildiği için User sınıfı içerisinde override edilerek gövdesi değiştirildi. override edilmiş bir metot içerisinden metodun orijinalini (temel sınıf içerisindeki halini) çağırabilmek için base anahtar sözcüğü kullanılır.

Konsol uygulamamızı tekrar çalıştıracak olursak ekran çıktısı aşağıdaki gibi olacaktır.

Serdar YILMAZ

“var” Anahtar Sözcüğünün Performansa ve Okunabilirliğe Etkisi

Visual Studio’da kullanmakta olduğum ReShapper uzantısının; int, double, string, bool gibi veri tiplerinde tanımlanmış değişkenlerde dahi “var” anahtar sözcüğünün kullanımını önermesi üzerine, “var” anahtar sözcüğünün performans üzerinde bir artısı olup olmadığını araştırıp, konuya ilişkin bir içerik yayınlamaya karar verdim. Devamını Okumak İçin Tıklayınız…

C# – Arayüzler

.netArayüzler, sınıflara rehberlik etmek üzere oluşturulan nesneye dayalı programlamanın en önemli özelliklerinden biridir. Sınıfların hangi metotları ve özellikleri içermesi gerektiğini arayüzler içerisinde bildiriyoruz.

Arayüz Oluşturma

Arayüzler “interface” anahtar sözcüğü ile oluşturulur. Zorunlu olmamakla birlikte arayüz isimleri genellikle “I” harfiyle başlatılır. Böylece “I” ön ekini gören bir programcı onun bir arayüz olduğunu anlar. Arayüz içerisinde özelliklerin ve metotların sadece bildirimi yapılır. Yani herhangi bir şekilde özelliklere bir değer atanmaz sadece türleri ve isimleri yazılır, aynı şekilde metotların içerisine kodlar yazılmaz sadece geri dönüş türleri ve isimleri yazılır. 

Arayüzlerin Sınıflara İmplement Edilmesi

IKisi arayüzü, Yonetici sınıfına implement edildiği için (1.Satır) Yonetici sınıfı, IKisi arayüzünde bildirimi yapılmış olan özellikleri ve metotları içermek zorundadır. Yonetici sınıfı içerisine, IKisi arayüzünde bildirimi yapılmış metotlar ve özellikler haricinde Yonetici sınıfına has metotlar ve özellikler de tanımlanabilir. 

Arayüzlere Neden İhtiyaç Duyarız?

Arayüzlerin geniş bir kullanım alanı bulunmaktadır. Basit bir örnek üzerinden anlatacak olursak; 

Arayüz kullanımı sınıflarımızı bir standart çerçevesinde yapılandırmamızı sağlamaktadır. IKisi arayüzünün Isci ve Yonetici sınıflarına implement edildiğini gören bir programcı, bu sınıfların içerisinde adSoyad, adres, departman gibi özelliklerin olduğunu ve bu özelliklere erişilerek gerekli bilgilerin alınabileceğini bilecektir.

Örneğin, Yonetici ve Isci sınıflarından oluşturulan nesnelerin içerisindeki adSoyad özelliğini ekrana yazdıran bir metot yazalım. Arayüzlerin sağlamış olduğu kolaylıktan faydalanmıyor olsaydık bu işlemi aşağıdaki gibi yapıyor olurduk;

İki farklı metot tanımlamamız gerekirdi. Bu metotlardan biri Yonetici sınıfı türünden parametre alırken, diğeri Isci sınıfı türünden parametre alırdı. Ancak arayüzler sayesinde bu işlemi tek bir metot yazarak yapabiliriz.

IKisi arayüzü, Yonetici ve Isci sınıflarına implement edildiği için bu sınıflardan oluşturulan nesneler, IKisi arayüzü türündeki bir parametrede tutulabilir. Bu yüzden adSoyadBilgisi() metoduna parametre olarak Yonetici veya Isci sınıfından oluşturulan nesneleri gönderebiliriz. Bu parametre aracılığı ile ilgili nesnelerin sadece IKisi arayüzünden gelen özellik ve metotlarına erişebiliriz.

Çoklu İmplementasyon 

Bir sınıfa birden fazla arayüz implement edilebilir. Örnek bir senaryo üzerinden konuyu anlatacak olursak; Bir fabrika için otomasyon programı yazdığımızı düşünelim. Fabrikada 3 farklı çalışan türü olsun, bunlar yönetici, işçi ve robot. Her bir çalışanın ID, ad-soyad, adres, maaş, departman ve toplam çalışma saati bilgisi olsun. 

Eğer arayüz tasarımını yukarıdaki gibi yaparsak ICalisan arayüzünü, oluşturacağımız Yonetici ve Isci sınıflarına implement edebiliriz ancak Robot sınıfına implement edemeyiz. Çünkü robotların adlarının ve adreslerinin olmayacağını ve maaş almayacaklarını biliyoruz. Bu yüzden ICalisan arayüzünü uygun bir şekilde parçalamamız gerekiyor. 

ICalisan ve IKisi şeklinde iki arayüz oluşturduk. Yöneticiler ve işçiler hem çalışan hemde birer kişi olduğundan, bu sınıflara hem ICalisan arayüzünü hem de IKisi arayüzünü imlement edeceğiz. Robot sınıfına ise sadece ICalisan arayüzünü implement edeceğiz. 

Kurumsal Mimarilerde Arayüz Kullanımı

Daha önce kurumsal mimaride bir uygulama geliştirmediyseniz bu bölümü şimdilik atlayabilirsiniz. 

Bu başlıkta olabildiğince yalın bir şekilde arayüzlerin kurumsal mimarilerde ki kullanımına bir örnek vermeye çalışacağım. Kod kalabalığını arttırmamak ve konuyu daha anlaşılır bir şekilde anlatmak için sınıfları karışıklığa yer vermeyecek şekilde isimlendirip, konumuzun dışında olan kod satırlarına/bloklarına (veritabanı sorguları) yer vermemeye çalışacağım.

Bir projede farklı veritabanı yönetim sistemleri kullanılabilir. Projelerimizi bizden fazla veritabanı yönetim sistemine destek verecek şekilde geliştirebilmek için arayüzlerden faydalanabiliriz. 

Öncelikle IRepository adını verdiğimiz arayüzün içerisinde, veritabanı üzerinde ekleme, silme, güncelleme, kaydetme gibi temel işlemleri yapacak metotların bildirimini yapıyoruz. Sonra bu arayüzü, farklı veritabanı yönetim sistemleri üzerinde işlem yapacak olan sınıflara implement ediyoruz. 

Projemizin hem Oracle veritabanına hemde MsSQL veritabanına destek vermesini istiyorsak; IRepository arayüzü içerisinde bildirimi yapılan metotları, OracleDB sınıfında Oracle vertabanında işlem yapacak şekilde, MsSQLDB sınıfında MsSQL veritabanında işlem yapacak şekilde yazmamız gerekiyor.

MsSQLDB ve OracleDB sınıfları ile artık hem MsSQL veritabanında hem de Oracle veritabanında işlem yapabiliyoruz.

Son olarak ExampleManager isimli bir sınıf oluşturuyoruz, bu sınıftan nesne oluştururken yapıcı metoduna parametre olarak MsSQLDB sınıfından bir nesne gönderirsek ekleme, silme, güncelleme, kaydetme işlemini MsSQL veritabanı üzerinde yapacaktır, OracleDB sınıfından bir nesne gönderirsek de Oracle veritabanı üzerinde yapacaktır. 

Ekran Çıktısı:

Serdar YILMAZ