Smart Enum, Strongly Typed Enum Nedir ?

Enumeration projelerimizde sıkca kullandığımız value-type'lardan biridir. .net framework'de sabit bir değer kümesinden oluşan ve basit karşılaştırmalar yapmak için kullandığımız temelde compile-time'da type safety sağlamak için tanımlanan özel bir sınıf türüdür. Örnek olarak; ToString, GetValues, Parse, TryParse gibi metodlar içerir.  

Örnek bir enumeration tanımlayacak olursak; Eft/Havale işlemleri yapan bir proje geliştiriyorsunuz ve bankalara ait eft kodlarını içeren bir enumeration tanımlayarak bu kodları ilgili enum'da tutmak istedik. Türkiyedeki bankaları ele alacak olursak;

public enum Bank
{
    Ziraat = 10,
    Garanti = 62,
    Isbank = 64,
    YapiKredi= 67,
}

vs şeklinde giden bir enumeration'ımız olsun. Tamda istediğimiz gibi aslında int olarak code’ları bulundurması gerekiyor ve banka bazlı değelerimizi tanımlamış olduk. Ek olarak bu bankalara ait isimleride biyerde tutalım dendi. Enum'lar property üzerinde attribute tanımlamaya olanak sağlıyor Description attribute'ü kullanarak yapabiliriz.

public enum Bank
{
    [Description("Ziraat Bankası")]
    Ziraat = 10,
    [Description("Garanti Bankası")]
    Garanti = 62,
    [Description("İş Bankası")]
    Isbank = 64,
    [Description("Yapı Kredi Bankası")]
    YapiKredi = 67,
}

Bu değerleri okumak içinde bir extension metod yazarsınız ve okursunuz vs. Ancak yapabileceklerimiz aslında sınırlı, bir kaç gün sonra bankanın shortName'i de gelsin yada bankaların eft saatleride yazsın vs. farklı bir custom attribute tanımlayarak bir çözüm üretebilirsinizde. Ancak bu hem okuması zor hemde development olarak sürekli bir extension metod ve akabinde reflection‘lar ile bu değerler okumamız gerekecekdir. Lots of attributes..Can you imagine it ?

Bu gibi problemlere çözüm olarak önerilen yöntem ise Type Safe or Strongly Typed Enum’lar tanımlamak. Steve (ardalis) Smith yazılarında detaylıca bahsettiği ve benimde ilk olarak o yazılardan araştırmaya başladığım smart-enum'lar içerisinde read-only property'ler bulunan bir class tanımlayarak kendi type-safe enumeration'larını yaratabileceğimizi belirtmekte. Bize sağladığı en iyi çözüm içerisinde enum'ın özelliklerini barındıran istediğimiz kadar property tanımlayıp kendi business’larını burada geliştirmemize olanak sağlar ve böylelikle attribute-reflection vs kullanmadan daha basit ve bir çeşit ValueObject based bir kullanıma olanak sağlamakta.

Örneğimize geri dönecek olursak; smart enum tanımlarken dikkat etmemiz gereken en önemli konu safety. SmartEnum class'ını sealed olarak tanımlayıp herhangi bir inheritance alınmasının önüne geçmeliyiz ve constructor başta olmak üzere bütün property'lerinin setter'larını private olarak tanımlayarak unsafe bir şekilde initialize edilmesine engel olacak şekilde tanımlamalıyız.

public sealed class Bank
{
    public static Bank Ziraat { get; } = new Bank(10, "Ziraat", "Ziraat Bankası", "08:30 – 17:00");
    public static Bank Garanti { get; } = new Bank(62, "Garanti", "Garanti Bankası", "09:00 – 17:15");
    public static Bank Isbank { get; } = new Bank(64, "Isbank", "İş Bankası", "09:00 – 17:00");
    public static Bank YapiKredi { get; } = new Bank(67, "YapiKredi", "Yapı Kredi Bankası", "09:00 – 16:45");

    private Bank(int eftCode, string name, string description, string processingTime)
    {
        EftCode = sode;
        Name = name;
        Description = description;
        ProcessingTime = processingTime;
    }

    public int EftCode{ get; }
    public string Name { get; }
    public string Description { get; }
    public string ProcessingTime { get; }
}

Klasik bir enumeration için sıkca kullandığımız bazı özellikleride smart enum'a kazandıracak olursak;

public sealed class Bank
{
    public override string ToString() => Name;
    public static IEnumerable<string> GetNames() => GetValues().Select(role => role.Name);
    public static Bank GetValue(int code) => GetValues().First(role => role.Code == code);
    public static Bank GetValue(string name) => GetValues().First(role => role.Name == name);

    public static IReadOnlyList<Bank> GetValues()
    {
        return typeof(Bank).GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Select(property => (Bank)property.GetValue(null))
            .ToList();
    }

    public static explicit operator int(Bank role) => role.Code;
    public static explicit operator Bank(int code) => GetValue(code);
}

Bu fonksiyonlarla birlikte Bank smart-enum'ının ihtiyacı olam bütün özelliklerini kazandırmış olduk.

Enumeration'lar switch-case statement'ı ile birlikte oldukça sık kullanılırlar, bizde aşağıdaki gibi Bank smart-enum'ını switch statement ile birlikte kullanabiliriz.

var value = Bank.Garanti;
switch (value)
{
    case var _ when value == Bank.Garanti:
        break;
    case var _ when value == Bank.Isbank:
        break;
    case var _ when value == Bank.Ziraat:
        break;
    case var _ when value == Bank.YapiKredi:
        break;
}

Özetleyecek olursak; enum tipi yerine smart-enum'ları kolay bir şekilde projelerinizde tanımlayabilirsiniz ve başta Serializable olmak üzere daha birçok özelliğide smart-enum'larınıza kazandırabilirsiniz. Ancak şu demek değildir ki bütün enum tipinde olan sınıflarınızı smart-enum'a çevirin. It depends on the business ! yani gerçekten ihtiyacınız varsa işinize yarayacaksa; yukarıda yazıdaki örnek misali gayet saf duygularla oluşturduğunuz bir enum almış başını gidiyor, attribute'lerle dolup taşma ihtimali gibi bir durum söz konusu ise enum tiplerinizi strongle typed enum sınıflarına çevirip daha esnek ve gelişime açık kullanışlı yapılar tasarlayabilirsiniz. 

source

Comments (4) -

  • Hocam teşekkürler faydalı bilgiler için.
    • Değerli yorumunuz için teşekkürler.
  • Allahın enumuna 2-3 field eklemek icin bunca tatava.. c# üzdün kardeshhim
    • Değerli yorumunuz için teşekkürler gtüredi Smile

Add comment