İlkay İlknur

just a developer...

C# 5.0 - Caller Info Attributes ile Kolaylaşan INotifyPropertyChanged İmplementasyonu

Windows 8, Windows Phone 7, Silverlight ve WPF ile uygulama gelistirirken kullandigimiz ve MVVM gibi senaryolarda da siklikla basvurdugumuz INotifyPropertyChanged implementasyonu çogu zaman eger bir kod snippeti kullanmiyorsaniz biraz can sikici ve zaman kaybedici bir implemantasyon olabilmekte.

INotifyPropertyChanged interface'ini neden kullandigimizi kisaca hatirlamamiz gerekirse, elimizde bulunan tiplerin binding islemlerinde içlerinde barindirdiklari propertylerde bir degisiklik oldugu durumlarda bagli olduklari UI kontrolü üzerinde durumlarini  güncellemeleri için gerekli olan elemanlari saglamakta INotifyPropertyChanged interface'i.

INotifyPropertyChanged interface'inin içerisine baktigimizda ise çok basit bir sekilde sadece 1 event ile karsilasiyoruz.

namespace System.ComponentModel
{
    public interface INotifyPropertyChanged
    {
        event PropertyChangedEventHandler PropertyChanged;
    }
}

Evet interface implementasyonu aslinda sadece 1 eventin tetiklenmesini gerektiriyor. Bu eventi tetiklerken bir de hangi propertynin degerinin degistigini de eventi tetikleme sirasinda bildirmemiz gerekiyor ki bizim eventimizi dinleyen kontroller degisen propertynin hangisi oldugunu algilayabilsin ve buna göre de sadece degisen propertynin degerini ekranda güncellesin.

Simdi hemen basitçe Insan isminde bir tip tanimlayalim ve bu tip içerisinde Ad ve Soyad isimli property ekleyelim ve bu propertylerde de degisiklik oldugunda bind edildikleri kontrollerde de ilgili degisikliklerin yansitilmasi için bir de INotifyPropertyChanged interface'ini implemente edelim.

public class Insan : INotifyPropertyChanged
{
    private string ad;

    public string Ad
    {
        get { return ad; }
        set
        {
            ad = value;
            RaisePropertyChanged("Ad");
        }
    }

    private string soyad;

    public string Soyad
    {
        get { return soyad; }
        set
        {
            soyad = value;
            RaisePropertyChanged("Soyad");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

Evet implementasyon bu kadar basit. Farketmis oldugunuz ilk sey eminim ki auto-implemented property kullanmayisimizdir. Çünkü burada ilgili propertye deger atanma sirasinda araya girmemiz gerekmekte. Bu nedenle auto-implemented property kullanamiyoruz. Bunun haricinde ise interface içerisinde bulunan eventi gerekli parametrelerle tetiklemek için RaisePropertyChanged isimli bir de yardimci metot yazdik. Bu metotta parametre olarak içerigi degisen propertynin ismini almakta. Daha sonra ise yazmis oldugumuz bu metodu propertylerin setterlari içerisinde çagirmaktayiz.

C# 5.0 - Caller Info Attributes ile Basitlesen Implementasyon

Yukaridaki implementasyona baktigimizda aslinda bize zaman kaybettiren ve interface'in implementasyonunu zorlastiran nokta aslinda RaisePropertyChanged metodunu çagirirken sürekli olarak degisen propertynin ismini vermemiz.

Simdi C# 5.0 ile beraber CallerInfo Attributes konusunu hatirlarsak aslinda Caller Info Attributes bize yapilan bir çagrim  ile ilgili olarak bilgi vermekteydi. Yani çagrim yapilan yerin satiri, bulundugu dosya ve çagrimda bulunan üyenin adi gibi alanlari saglamaktaydi. Sanki çagrim yapan üyenin adi tam bizim ihtiyacimiz olan sey :) O zaman RaisePropertyChanged metodunu su sekilde degistirsek nasil olur bir bakalim :)

public void RaisePropertyChanged([CallerMemberName]string propertyName = null)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
    }
}

Gördügümüz gibi bu sekilde artik metodu çagiran üyenin adini C# 5.0 ile beraber gelen Caller Info Attributes ile alabilmekteyiz. Insan tipi içerisinde bulunan propertylerin setterlarinda da artik RaisePropertyChanged metodunu parametre vererek çagirmamiza gerek yok. Çünkü compiler bu parametreyi artik kendisi arka planda ilgili metoda opsiyonel parametre degeri olarak geçirmekte.

Son olarak Insan tipinin içerigi su sekilde olmakta.

public class Insan : INotifyPropertyChanged
{
    private string ad;

    public string Ad
    {
        get { return ad; }
        set
        {
            ad = value;
            RaisePropertyChanged();
        }
    }

    private string soyad;

    public string Soyad
    {
        get { return soyad; }
        set
        {
            soyad = value;
            RaisePropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName]string propertyName = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
    }
}

Baktigimizda aslinda programalama dili tarafinda gelen bir yenilik bambaska bir tarafta belki de hiç aklimiza gelmeyecek bir noktada gördügünüz gibi isimizi kolaylastiriyor. Iste programlama dillerinin de en güzel yani bu :)

Hepinize kolay gelsin :)



C# 5.0 - Caller Info Attributes

Merhaba Arkadaşlar,

Şu ana kadar C# 5.0 ile ilgili makalelerimizde sizlerle C# 5.0 ile beraber gelecek olan Asenkron Programlama yeniliklerini inceledik. Her ne kadar C# 5.0'ın teması Asenkron Programlama olsa da C# 5.0 ile beraber gelecek olan bir programlama dili yeniliği daha bulunmakta.

Asenkron Programlama'nın yanında biraz üvey evlat muamelesi  :) görüyor olsa da bu yeniliğin adı Caller Info Attributes.

Geliştirdiğimiz uygulamalar içerisinde olmazsa olmaz dediğimiz Cross Cutting bölümlerden biri de Loglama bölümüdür. Geliştirdiğimiz uygulamaların büyüklüğüne, hedef kitlesine, konseptine bağlı olarak içerlerinde yapmış olduğumuz loglamalar daha da derinleşebilmekte veya belirli sınırlar dahilinde kalabilmektedir.

__FILE__ , __LINE__ Macrolarının C# Karşılığı ?

Eğer daha önce C, C++ gibi dillerle uğraştıysanız bu programalama dilleri içerisinde yaptığınız çağrımların, bulunduğu satır numarasını veya bulunduğu dosyanın pathi gibi bilgileri yukarıda görmüş olduğumuz gibi macroları kullanarak alabilmekteyiz. Peki bu macroların C# tarafında karşılığı var mı ?

Cevabımız hayır :) Yukarıdaki macroların yaptıklarını aslında şu anda Reflection kullanarak kendimiz de gerçekleştirebilmekteyiz. Ancak programlama dili tarafında C#'ın 4.0 sürümü de dahil olmak üzere istediğimiz bu fonksiyonaliteyi  otomatize eden, doğrudan arka planda kendisi sağlayan bir yapı bulunmamakta.

Tabi bizler de developer olarak işlerimizi hızlandırması açısından bu macroları C# içerisinde kullanabilmeyi istiyoruz. İşte C# 5.0 ile beraber gelen Caller Info Attributes özelliği ile bu fonksiyonaliteleri artık compiler kendisi otomatize etmekte ve developerların kullanımına sunmakta.

Peki kullanım aynı C ve C++'ta olduğu gibi __FILE__,__LINE__ macroları şeklinde mi olmakta ? Bu sorunun cevabı  da hayır :) Çünkü macrolar C# ve VB içerisinde desteklenmiyor ve desteklenmeyecekte. Bunun yerine Attribute mantığını kullanarak hızlı bir şekilde implementasyonlarımızı gerçekleştirebiliyoruz.

Caller Info Attributes özelliği kapsamında System.Runtime.CompilerServices namespace'i içerisinde  3  yeni attribute ile karşılaşmaktayız.

Bunlar,

[CallerFilePath] : Çağrım yapılan yerin bulunduğu dosyanın pathini verir.

[CallerLineNumber] : Çağrım yapılan yerin bulunduğu dosya içerisindeki satır numarasını verir

[CallerMemberName] : Çağrım yapan üyenin adını verir.

Bu 3 attribute'ün de target type'ı Parameter olarak set edilmiştir. Yani sadece metot imzalarındaki parametrelere bu attributeleri verebiliriz.

Peki buna göre kullanımı nasıl yapacağız ?

Aslında herşey oldukça basit. Attributeleri C# 4.0 ile beraber gelen Optional Parameters özelliği ile beraber kullanacağız.

Evet çok uzatmadan kod kısmına geçelim.

[sourcecode language="csharp"] static void Log(string message, [CallerFilePath]string path = null, [CallerLineNumber]int line = 0, [CallerMemberName]string memberName = null) { Console.WriteLine(String.Format(@"Message:{0}\n FilePath:{1}\n LineNumber:{2}\n MemberName:{3}", message, path, line, memberName)); } [/sourcecode]

Yukarıda gördüğümüz gibi çok basit bir şekilde Caller Info Attributeleri kullanarak istediğimiz değerleri elde edebiliyoruz. Bunu yaparkende Optional Parameters özelliğini kullanıyoruz. Böylece optional olan parametreyi metot çağrımı sırasında boş geçtiğimizde compiler arka planda ilgili parametreye attribute özelliğine göre ilgili değeri atıyor.

Şimdi isterseniz yukarıda yazmış olduğumuz Log metodunu basit bir console uygulamasından çağıralım ve sonucu görelim.

[sourcecode language="csharp"] static void Main(string[] args) { Log("Caller Info Attributes kullanıyoruz !"); } [/sourcecode]

Uygulamayı çalıştırdığımızda ise aşağıdaki gibi bir sonuçla karşılaşmaktayız.

Console uygulamasında Log metodunu çağırırken gördüğünüz gibi path,line ve memberName parametrelerini metoda geçirmedik. Böylece de compiler arka planda kendisi bu alanların karşılıklarını otomatik olarak atadı.

C# 5.0 ile beraber gelen Caller Info Attributes özelliği gördüğümüz gibi biz developerların özellikle loglamalar sırasında  sıklıkla kullanacağı özelliklerden biri olma konusunda kuvvetli bir aday. Özellikle işimizi oldukça kolaylaştırdığınız söylememiz herhalde yanlış olmaz. ;)

C# 5.0 ile ilgili yazılarımız şimdilik bu kadar. Gelişmeler, iyileştirmeler ve ekstra yenilikler oldukça blog üzerinden inceliyor olacağız.

Görüşmek Üzere,