Öznitelikler ve Reflection’la C# Dilini İhtiyaçlarınız Doğrultusunda Genişletin -2

   Bir önceki makalemde sizlere öznitelik (attribute) ve yansıma (reflection) konularında bahsetmiştim. Her zaman dediğim gibi, bu ikili bir arada gerçekten çok önemli bir güç haline gelmekte. Bu ikilinin kullanımına dair o kadar fazla şey söylenebilir ki, tek makale buna hiç bir zaman yetmeyecektir. Bu sebeple ikinci bir makale ile konuyu biraz daha derinleştirmek istedim.

   Hatırlarsanız önceki makalemde öznitelik ve reflection’la C# diline yeni bir anahtar kelime ekleme ihtiyacı olmazsızın özellikler ekleyebileceğinizden bahsetmiştim. Hatta bu yaklaşımın Microsoft tarafından da benimsendiğini SerializableAttribute’ü örnek göstererek anlatmıştım. Xml serialization sırasında kullanılan öznitelikler, asmx ve wcf servislerinde kullanılan öznitelikler, hatta konsol uygulamalarında kullandığınız STAThreadAttribute’ü… Örnekler bu şekilde uzayıp gidiyor.

   İlk makalemde sizlerle paylaştığım örneği hatırlayalım;

public class Ogrenci {
    [ZorunluAlan]
    public string Adi;

    [ZorunluAlan]
    public string Bolum;
}

   Bu örnekte kullandığımız öznitelikler her ne kadar kod içerisine meta veri eklese de, yapılan işe baktığımızda daha çok bir işaretleme söz konusu. Evet, işaretleme; Ogrenci sınıfım içerisindeki Adi ve Bolum alanlarını zorunlu olarak işaretliyorum. Daha sonrada bu işaretlediğim alanlar üzerinde doğrulama yapıyorum.

   Öznitelikler ve reflection’la yapıbileceğiniz şeyler bu işaretlemelerden çok daha fazlası aslına bakarsanız. Öznitelikler, yapısal olarak bakıldığında birer sınıftır ve sınıf içerisinde metodlar oluşturabilir, özellikler ve alanlar içerisinde veri tutabilirim; hatta istediğim arayüzü uygulayabilir, bir özniteliği başka bir öznitelikten kalıtabilirim. Bu makalemde bu konulara değinmek istiyorum.

   Bir sınıf içerisindeki string alanlar için zorunluluk kontrolü yapabilmeye olanak sağlayan ve bizim oluşturduğumuz ZorunluAlanAttribute’ü mantığımızı isterseniz bir adım ileri taşıyalım. Sınıf alanları için doğrulama yapabilme mantığımızı bir önceki makalede yer alan static ZorunlulukKontrolu sınıfı içerisindeki Dogrula metodunda oluşturmuştuk. Bu yaklaşımımızda öznitelikleri sadece işaretleme amacıyla kullanmış ve işaretlenmiş alanlar için doğrulama işlemini ZorunlulukKontrolu.Dogrula metodu içerisinde oluşturmuştuk. Önceki makalemde sizlerle paylaştığım iş mantığı için bu yaklaşım doğru olsa da bu durum daha fazla doğrulama ihtiyacının olduğu iş mantıklarında bizi dar boğaza sokabilir; çünkü bu yaklaşımda tüm doğrulama işi tek bir sınıfta olmakta… genişletilebilir değil… Ekip çalışmasında aynı sınıf üzerinde çalışmak zaman zaman sorun oluşturabilir..

   Peki bu durumda nasıl bir yaklaşım izlenmeli? Sizlere makalemin başında da belirttiğim gibi öznitelikler’e yapısal olarak bakacak olursanız birer sınıftırlar. Eğer istersek içlerine metod ekleyebilir, bu metodları çağrırarak iş yaptırabiliriz. Bu durumda yapılacak en akıllıca hareket, doğrulama iş mantığımızı ZorunlulukKontrolu statik sınıfı içerisinden alarak özniteliklere taşımak olacaktır. Öznitelikler içerisindeki bu doğrulama mantığına ulaşıp çalıştırabilmek için ise ortak bi kontratımızın olması gerekecektir. Bu sayede gelen sınıfın doğrulama kontratına uyduğunu bilerek ilgili metodunu çağrıbiliriz.

   Gerçek hayattaki bir kontrattan bahsediyorsak, bunun C# dünyasında bir kaç karşılığı bulunmakta;

  • Arayüz
  • Temel sınıf
  • Abstract temel sınıf

  Bu yaklaşımların her birinin kendine göre artıları ve eksileri olamasına karşın, bu makale için abstract temel sınıf’ı kontratımı belirtmekte kullanacağım. Bu sayede hem özniteliklerin tanımlama bilgilerini hem de tüm doğrulama özniteliklerimde yapmak isteyebileceğim diğer ortak işleri tek bir çatı altında gerçekleştirebilirim.

   Tasarım olarak tüm doğrulama özniteliklerinin abstract base class olarak tanımlanmış olan AlanDogrulamaAttribute sınıfından türetelim;

[AttributeUsage(
    AttributeTargets.Field,
    AllowMultiple = false,
    Inherited = true)]
public abstract class AlanDogrulamaAttribute : Attribute {
    internal protected abstract bool Dogrula(object alanDegeri);
}

   6. satırda tanımlanan Dogrula metodu AlanDogrulamaAttribute sınıfından türeyecek tüm alt sınıflar tarafından tanımlanmak zorundadır. Bu metod, tanımlandığı assembly içerisinde kullanabilmek adına internal olarak işaretlenirken farklı assembly’ler içerisindeki alt sınıflar tarafından görülüp tanımlanabilmesi için de protected olarak işaretlenmiştir. İsterseniz bu metodu public olarak da tanımlayabilirsiniz.

hiyerarsi

    Bu makale için örneklediğim bu abstract temel sınıfı genişleten ZorunluAlanAttribute, SabitUzunlukAttribute, MaksimumDegerAttribute ve MinimumDegerAttribute sınıfları bulunmakta. Bu dört sınıfın konunun kavranması adına yeterli olacağını düşünüyorum. Gerçek hayattaki örneklerde bundan daha derin bir kalıtım hiyerarşisi ve özniteliklerin tanımlanması ihtiyacı oluşabilir.

   Bu dört sınıf iş mantığı gereği gerçekleştirilmesi gereken doğrulama kodlarını kendi tanımladıkları Dogrula metodu içinde yapmaktadırlar. Dolayısıyla hem kodum daha anlaşılır, hem de genişletilmeye hazır olacaktır. Aşağıda bu dört sınıfa ait kodları bulabilirsiniz;

public class ZorunluAlanAttribute : AlanDogrulamaAttribute {
    protected internal override bool Dogrula(object alanDegeri) {
        return (alanDegeri is string && !string.IsNullOrEmpty(alanDegeri as string)) || alanDegeri != null;
    }
}

public class SabitUzunlukAttribute : AlanDogrulamaAttribute {
    private long uzunluk;

    public SabitUzunlukAttribute(long uzunluk) {
        this.uzunluk = uzunluk;
    }

    protected internal override bool Dogrula(object alanDegeri) {
        if (alanDegeri is string) {
            var alanDegeriString = alanDegeri as string;
            return !string.IsNullOrEmpty(alanDegeriString) && alanDegeriString.Length == uzunluk;
        }

        return true;
    }
}

public class MaksimumDegerAttribute : AlanDogrulamaAttribute {
    private long maksimumDeger;

    public MaksimumDegerAttribute(long maksimumDeger) {
        this.maksimumDeger = maksimumDeger;
    }

    protected internal override bool Dogrula(object alanDegeri) {
        return !(alanDegeri is long) || ((long)alanDegeri) <= maksimumDeger;
    }
}

public class MinimumDegerAttribute : AlanDogrulamaAttribute {
    private long minimumDeger;

    public MinimumDegerAttribute(long minimumDeger) {
        this.minimumDeger = minimumDeger;
    }

    protected internal override bool Dogrula(object alanDegeri) {
        return !(alanDegeri is long) || ((long)alanDegeri) >= minimumDeger;
    }
}

   Paylaştığım bu kodlarda detaylı bir doğrulama ve hata kontrolü yapılmamış olup, makalenin ana konusunun anlaşılması adına temel kontrolleri yapan kodlar eklenmiştir. Gerçek hayatta daha detaylı doğrulama ve hata kontrolü yapılması gerekecektir.

   Doğrulama özniteliklerinde kullanılacak olan kontratımın ve bu kontrata uyan sınıf tanımlarımın ardında en önemli nokta olan ZorunlulukKontrolu.Dogrula metodu içerisinde kodumu yeni iş mantığım çerçevesinde güncellemek;

public static class ZorunlulukKontrolu {
    public static bool Dogrula(object dogrulanacakOrnek) {
        Type dogrulamacakTur = dogrulanacakOrnek.GetType();

        FieldInfo[] dogrulanacakTurAlanlari = dogrulamacakTur.GetFields(
                                                BindingFlags.Public |
                                                BindingFlags.Instance);


        foreach (FieldInfo dogrulanacakTurAlani in dogrulanacakTurAlanlari) {
            object[] alanDogrulamaOznitelikleri = dogrulanacakTurAlani.GetCustomAttributes(typeof(AlanDogrulamaAttribute), true);

            if (alanDogrulamaOznitelikleri.Length != 0) {
                object alanDegeri = dogrulanacakTurAlani.GetValue(dogrulanacakOrnek);

                foreach (AlanDogrulamaAttribute alanDogrulamaOzniteligi in alanDogrulamaOznitelikleri) {
                    if (!alanDogrulamaOzniteligi.Dogrula(alanDegeri)) {
                        return false;
                    }
                }
            }
        }

        return true;
    }
}

   Önceki makalemde paylaştığım ZorunlulukKontrolu.Dogrula metodunda farklı olarak bu sefer alan üzerinde AlanDogrulamaAttribute özniteliği aranmakta. Üstelik bu yapılırken yukarıda paylaştığım ve AlanDogrulamaAttribute sınıfından türetilen diğer sınıflarda gelebilmekte. Buradaki püf noktası; reflection ile bir alan üzerinden özel özniteliklerin sorgulanması durumunda bu öznitelikten türetilen özniteliklerinde getirilebiliyor olmasıdır. Ardından bir doğrulama sınıfı bulunması durumda bu sınıf içerisinde bulunan Dogrulama metodu çağırılmakta. İlk başarısız doğrulamada bu metod false değerini dönecektir. Herhangi bir doğrulama özniteliği bulunamaması ya da tüm doğrulamalardan geçilmesi durumunda ise true değeri dönecektir.

   Ogrenci sınıfımız içerisinde ögrenci numarasının tutulacağı long türünden yeni bir alan ekleyerek bu alanın değerinin 199800001 ile 201099999 arasında olması durumunu doğrulamak istersek sınıfımız aşağıdaki gibi görülecektir;

public class Ogrenci {
    [ZorunluAlan]
    public string Adi;

    [ZorunluAlan]
    public string Bolum;

    [MaksimumDeger(201099999)]
    [MinimumDeger(199800001)]
    public long OgrenciNumarasi;
}

   Bu yeni yapıyla birlikte artık öznitelikler sadece işaretleme için kullanımayıp, içlerinde hem veri hem de iş mantığı barındıran akıllı sınıflar haline gelmiş durumda.

   Bu yeni yapı bize daha fazla esneklik sunarken, 3.parti kütüphanelerin de AlanDogrulamaAttribute sınıfını türeterek yeni doğrulama öznitelikler oluşturabilmesine de olanak sağlamaktadır.

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

3 yorum

  1. dhmm   •  

    Hocam yazilarinizin ve yaptiginiz mukemmellin otesinde isin devamini dilemekle birlikte size bir sorum olucak… 6 seneden beri c++ ile yaziyorum ve c# a gectim… Tabi duzeyim otomatikmen acemi oldu… Nereden baslamaliyim ???

    Tesekkurler

    • Fatih Boy   •     Yazar

      Merhaba dhmm,
      Bu makalemin konusu olmaması nedeniyle bu sorunu burada yanıtlamam doğru olmayacaktır. İletişim (Contact) bölümünden bana ulaşırsan yardımcı olmaya çalışayım.

  2. Yunus   •  

    Hocam, makalenizde böylesi bir konuyu çok güzel bir şekilde özetlediğiniz için teşekkür ederim.
    Elinize sağlık. Yazılarınızın devamı gelmesi dileğiyle.

Bir Cevap Yazın

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