C#’daki “[Attribute]” yani nitelik yaklaşımı dilin yorumlanması açısından güzel getirilere sahip bir özellik. Geliştiricinin, derleyiciye belli üst bilgileri(a.k.a metadata) aktararak, kodun derlenme ya da çalışma şeklini değiştirebiliyor olması yazılım çözümlerine ve yazılım geliştirme yöntemlerine çok büyük artılar sağlayabiliyor.

Genellikle .NET platformunun sunduğu hazır “[Attribute]”’ları kullanarak çözümlerimizi geliştiriyoruz; gerektiği yerlerde kendi özel [Attribute]’larımız ile de çözümlerimizi genişletebiliyoruz. Açıkçası çok derinlere girmeyeceğim, -ki zaten ihtiyaçlara göre sık sık tercih edebiliyoruz. Ama [Attribute]’ların öneminin ve değerli bir özellik olduğunun altını koyu bir kalemle altını çizmek isterim. Bu önemi ve farkındalığı biraz daha arttırabilmek için, faydalığı olduğunu düşündüğüm birkaç [Attribute]’dan hızlıca bahsetmeye çalışacağım. Hem [Attribute]’lar ile neler yapılabilir yeni fikirlerler vermesi, hem de farkında olunmayan birkaç niteliğin farkındalığı ile yazılım çözümleri için faydalı olur umarım bu yazı. Tabi ki bilenler, kodunda kullananlar illa ki vardır; onlar için biraz tekrar olacak belki ama en azından yorumlarını paylaşabilirlerse ekstra sevinirim, seviniriz. Değil mi?

Nitelikler(a.k.a [Attribute]), .NET ve Rosyln‘nın da gelişmesi ile statik kod analizi açısından da önemli bir yere geldi. Bahsetmeye çalışacağım bir tanesine bu açıdan da bakmakta fayda var.

[CallMemberName]

Bir metodu hangi metot çağırmış bunu bilebilmek kod içerisinde bazen ihtiyaç olabiliyor. En basitinden Log ve Trace ihtiyaçları için önemli. Kodun içerisine statik olarak belirtmek yerine(-ki hepimizin yapmışlığı vardır) [CallMemberName] niteliğiyle bir parametre ekleyerek bunu yapmak mümkün.

public class SomeGreatAPI
{
    public void Foo()
    {
        this.Log("Invoked in some method,which one?");
    }

    private void Log(string message, [CallerMemberName] string memberName="")
    {
        Console.WriteLine($"{message}");
        Console.WriteLine($"This message is from {memberName}");
    }
}

[ModuleInitializer]

Belki de çok gerekli gelmeyecek ama belli senaryolar için önemli olabilecek [ModuleInitializer] niteliğinden de bahsetmek isterim. Bu nitelikle işaretlediğimiz bir metot, ilgili olan “assembly” ilk yüklenirken çalışacak metot oluyor. Belli iş kurallarının kontrol edilmesi ya da ilk önce bazı değerlerin oluşturulup ayarlanması gibi senaryolar için güzel bir özellik. Tabi ki başka yaklaşımlarla da bu tarz bir ihtiyacı kısmen de olsa gerçekleştirebiliriz ama hem kodun derli toplu olması için hem de framework’ün kendi içindeki ideal bir yapı olduğu için tercih edilebilecek güzel bir ifade şekli diye düşünüyorum.

namespace Csharp10.Features
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

public class SomeGreatAPI
{
    [ModuleInitializer]
    public static void Init()
    {
        Console.WriteLine("Initialize...");
    }
}

Yukarıdaki basit örneği çalıştırdığımız zaman aşağıdaki gibi bir çıktı kodun daha iyi anlaşılmasını sağlayacaktır.

Initialize...
Hello, World!

Kod içerisinde [ModuleInitializer] ile belirttiğimiz metot ilk çalışacak metot. Dolayısıyla uygulama ilk çalıştığında belli kontroller ya da doğrulamalar yapmak için güzel bir fırsat. [ModuleInitializer] niteliği ile işaretleyebilmemiz için metodun birkaç gereksinimi var; parametre almayan, dönüş değeri olmayan “static” bir metot olması gibi…

[return:NotNullWhen(bool returnValue)]

Bu nitelik biraz değişik; API/SDK tarzı bir çözüm sunuyorsanız ya da bir Framework geliştiriyor ve bunu yazılımcılara sunuyorsanız, kullanan kişilere yol göstermesi için güzel bir nitelik diye düşünüyorum.

public class SomeGreatAPI
{
    [return: NotNullIfNotNull("email")]
    public static string? GetEMailProvider(string? email)
    {
        //return SomeOperationResult
    }
}

Yukarıdakine benzer bir metodu sunduğumuzu düşünelim. “Nullable” bir metot olup, aldığı parametre değerlerine göre null dönmesi gereken bir ihtiyaç olduğunu hayal edelim. E-Posta adresinin hangi servis sağlayıcıdan olduğunu bulmamız gereksin mesela… @gmail.com, @live.com gibi. Eğer e-posta parametresi verildiyse mutlaka bir değer dönsün, eğer parametre verilmediyse null dönsün. Bu metodu kullanacak geliştiriciler için bu mantığı [return:NotNullIfNotNull(parameterName)] niteliği ile anlatmak mümkün. Metodu aşağıdaki gibi normal bir şekilde çağırdığımızı düşünelim.

Her şey normal gözükecektir. Derleme adımında her hangi bir uyarı bile gözükmeyecektir. “nullable” olan email parametresini metodu çağırdığımız yerdeki iş kuralından dolayı doldurmadığımızı düşünelim.

Visual Studio direkt olarak “provider” değişkeninin altını çizecektir ve uyarı gösterecektir. Ya da zaten derlendiğimizde de kodu uyarı olarak bunu göreceğiz.

“Eeeee şimdi zaten null kontrolü yapılmalı” diye düşünebilirsiniz; -ki evet, belli kontrollerin daha güvenli kod yazmak için yapılıyor olması gerekli. Tecrübenin yanında, bu tarz bilgilendirmeler ile API’yı kullanmaya başlayan kişileri doğru bir şekilde bilgilendirmek mümkün. Ayrıca daha düzenli kod yazmak için de tercih edilebilecek bir özellik.

.NET Framework ile uzun süre çalışmış kişiler için bu yaklaşım biraz tanıdık gelecektir belki ve “Code Contracts”‘ı anımsatacaktır. .NET Framework’de kodların çalışma mekanizması içinde ön koşul ya da son çalışma adımı gibi ara tanımlar koyabiliyorduk. Ne yazık ki yeni .NET platformunda(>=5.0) şu an için pek mümkün değil. Ama az önce bahsettiğim nitelik(ler) ile benzer koşulları oluşturmak mümkün. Bu arada bu niteliğin sağlıklı çalışabilmesi için projede nullable özelliğinin aktif olması gerekmekte. Benzer başka nitelikler de .NET’in içinde mevcut. Merak uyandırdıysa eğer, onlara da göz atmanızı tavsiye ederim.

Bu yazı biraz kısa ve hızlı oldu ama “[Attribute]”ler konusunda farklı yaklaşımlar geliştirmek için biraz olsun motivasyon sağlar. Yakın zamanda bazılarının faydalarını gördüğüm için kendi adıma daha kalıcı olması için hızlıca paylaşmak istedim. Bir sonraki yazıda görüşmek üzere, mutlu kodlamalar… 😀😀