C#’ta Enum Tipine Genel Bakış

C#’ta enum tipi, sabitlerle isimlendirilmiş ayırt edici veri tipidir. Aşağıdaki örnek 3 sabit değer ile oluşturulmuş enum tipi olan Renk adında bir yapı tanımlayalım:

using System;

namespace ConsoleApplication1
{

enum Renk
 {
 Kırmızı,
 Yeşil,
 Mavi
 }

class Test
 {
 static void RenkBastır(Renk renk)
 {
 switch (renk)
 {
 case Renk.Kırmızı:
 Console.WriteLine("Kırmızı");
 break;
 case Renk.Yeşil:
 Console.WriteLine("Yeşil");
 break;
 case Renk.Mavi:
 Console.WriteLine("Mavi");
 break;
 default:
 Console.WriteLine("Bilinmeyen renk");
 break;
 }
 }

static void Main()
 {
 Renk r = Renk.Kırmızı;
 RenkBastır(r);
 RenkBastır(Renk.Mavi);
 }
 }
}
 

Her enum tipi underlying type adı verilen bir enum tipi ile ilişkilendirilir. Bir enum, underlying type içerisinde formatını ve mümkün olan değerin uzunluğunu tutar. Varsayılan olarak;

  • Her underlying değerleri int tipindedir.
  • Sabitler 0, 1, 2, 3.. enum üyelerinin tanımlama sıralarına göre otomatik olarak atanır.

Aşağıdaki örnek, sbyte tipinde bir underlying type ile Hiza adından bir enum tipini tanımlar;

enum Hiza : sbyte
 {
    Sol = -1,
    Orta = 0,
    Sağ = 1
 } 

Bu örnekte de görüldüğü gibi, bir enum üyesi tanımlama aşamasında sabit bir değere atanabilir. Burada atadığımız her sabitin underlying type (sbyte) tipinin aralığı içerisinde bulunmak zorundadır. Eğer bir enum tipi tanımlamasında açıkça belirli bir değere atanmamışsa, ilk değeri belirtmek için 0 (sıfır) değeri verilir ve tanımlama sıralamasına göre 1 artarak ilerler.


int i = (int)Renk.Mavi; // int i = 2;
Renk r = (Renk)2; // Renk r = Renk.Mavi

Enum tipleri için 0 özel bir değere sahiptir. Bu değer bir enum tipine atandığında otomatik olarak bu int değeri bir enum tipine çevrilir. Örneğin;


Renk r = 0;
Console.WriteLine(r);

Şeklinde bir kod yazdığımızda bu kodun çıktısı “Kırmızı” olur. Bu atama işlemini Hiza enum’u ile denemiş olsaydık bu enum’da bulunan Orta değeri 0 değerine atandığı için bu sefer ilk elemanı değil Orta değerini ekrana yazdırmış olacaktır. Fakat bu atama ve çevrilme işlemleri sadece 0 değeri için geçerlidir. Bu yüzden aşağıdaki kodumuz “Cannot convert type int to Renk” şeklinde bir hata verecektir.


Renk r = 1;
Console.WriteLine(r);

0’a enumlar tarafından bu şekilde davranmalarının iki nedeni vardır:

  • Bir enum’un ilk üyesi sıklıkla enum’un default value’su olarak kullanılır.
  • Birleşik enum tipleri için 0’ın anlamı “Flag Yok”

Ayrıca bu çevirme işlemlerini boolean tipler için de kullanabiliriz:


int i = (int)Hiza.Sol;
Hiza h = (Hiza) i;
bool Solmu = (int) h == -1;
Console.WriteLine(Solmu);

Burada da öncelikle Hiza enumunda yer alan Sol üyesinin üye sıralamasındaki yeri alınır. (0 numaralı üye) Daha sonra bu sıralama değeri Hiza tipinde hangi değişkene denk geldiği belirlenir. (0. üye Sol adlı üye) Ardında da bu üyenin değerinin –1 olup olmadığı (int)h ile belirlenip bool değişkenine atanır.

Enum’ları başka bir enum tipine açıkça atayabiliriz. Örneğin; YatayHiza adında bir enum oluşturalım:


enum YatayHiza
 {
Sol = Hiza.Sol,
Sağ = Hiza.Sağ,
Orta
 }

Bu tanımlamadan sonra aşağıdaki şekilde tanımlamalar yapabiliriz:


YatayHiza y = (YatayHiza)Hiza.Sağ;
YatayHiza i = (YatayHiza)(int)Hiza.Sağ;

Burada y ve i değişkenlerini yazdırdığımızda ikisinde de “Sağ” sonucunu elde ederiz.

Flag Kullanımı

Enum üyelerini birleştirebiliriz. Karışıklığı önlemek için, enum üyelerini değerleri ile birleştiririz. Örneğin aşağıdaki enum tanımımızı yapalım:


YatayHiza y = (YatayHiza)Hiza.Sağ;
YatayHiza i = (YatayHiza)(int)Hiza.Sağ;

Enum üyelerini birleştirmek için | ve & gibi bir operatörlerini kullanırız. Bu yüzden bu üyelerin değerlerini 2’nin üsleri şeklinde tanımladık:


[Flags]
 enum Kenar
 {
Sol = 1,
Sağ = 2,
Üst = 4,
Alt = 8
 }

| operatörü ile SağSol enum değişkenini oluşturalım. SağSol ve Sol üyelerini & operatörü ile and’lersek ortak payda Sol olduğundan altındaki ifadeyi yazdırır. formatlı değişkeni “Sol, Sağ” olarak tanımlanır Kenar tipindeki enum’u string’e çevirdiğimizde. k değişkenini de farklı yolla SağSol değişkenine eşitledik. Son olarak ta k değişkenini Sağ değişkeni ile ^ (XOR) işlemine soktuğumuzda geriye sadece Sol kalır.

Kural ile, Flags niteliği bir enum’un üyeleri birleştiriliyorsa her zaman uygulanmalıdır o enum yapısına. Eğer Flags niteliği kullanmadan bir enum oluşturur ve yine bu üyeleri birleştirmek isterseniz sorun çıkmaz. Yani yukarıdaki enum tanımlamasında [Flags] kısmını silsek bile bu programımız sorunsuz çalışır. Fakat, bir enum örneği için ToString() metodunu çağırmak bir string yerine bize bir numara getirecektir.

Kural ile, birleştirilmiş enum tipi tekil değil de çoğul tipte isimlerden oluşmalıdır.

Ayrıca enum üyelerini enum içerisinde tanımlama aşamasında da birleştirebiliriz:


[Flags]
 enum Kenar
 {
 Sol = 1,
 Sağ = 2,
 Üst = 4,
 Alt = 8,
 SolSağ = Sol | Sağ,
 AltÜst = Alt | Üst,
 Tüm = SolSağ | AltÜst
 }

Enumlarla ilgili işlem yapabileceğimiz operatörler aşağıdaki şekildedir;

= == != < > <= >= + – ^ & | ˜
+= -= ++ – sizeof

Bit bir, aritmetik ve karşılaştırma operatörleri geriye underlying değerlerinin işlemler sonuçlarını geri döndürür.

Type-Safe Konusu

Aşağıdaki enum’ yapısını göz önünde bulunduralım;


enum Kenar
 {
Sol,
Sağ,
Üst,
Alt
 }

Bir enum kendi underlying type versine atanabildiği gibi dışarıdan gerçek bir değer de enum’a bound edilebilir. Örneğin;


Kenar k = (Kenar)12345;
Console.WriteLine(k);

kodu bize 12345 çıktısını verir. Fakat bit ve aritmetik operatörler benzer bir şekilde geçersiz bir değer üretirler. Bu yüzden;


Kenar k = Kenar.Sağ;

k++;

kodu herhangi bir hata oluşturmaz.

Bir enum değerinin geçerli olup olmadığı kontrol etmek için Enum.IsDefined statik metodunu kullanabiliriz. Örneğin;


Kenar kenar = (Kenar)1;
 Console.WriteLine(Enum.IsDefined(typeof(Kenar), kenar));
 Kenar kenar1 = (Kenar)12345;
 Console.WriteLine(Enum.IsDefined(typeof(Kenar), kenar1));
 Kenar kenar2 = Kenar.Sol;
 Console.WriteLine(Enum.IsDefined(typeof(Kenar), kenar2));

kodumuzun çıktısı sırayla “True, False, True” olur. İlk satırımızın neden True döndürdüğünü yine bu konu içerisinde anlatmıştık.

Ne yazık ki, Enum.IsDefined metodu flag kullanılmış enum yapılarında kullanılamaz. Fakat aşağıdaki gibi tanımladığımız FlagTanımlandımı adında bir metod ile bir enum yapısının flag kullanılıp kullanılmadığını kontrol edebiliriz:


[Flags]
 enum Kenar
 {
 Sol = 1,
 Sağ = 2,
 Üst = 4,
 Alt = 8
 }

class Test
 {
 static bool FlagTanımlandımı(Enum e)
 {
 decimal d;
 return !decimal.TryParse(e.ToString(), out d);
 }

static void Main()
 {
 for (int i = 0; i <= 16; i++)
 {
 Kenar kenar = (Kenar)i;
 Console.WriteLine(FlagTanımlandımı(kenar) + " " + kenar);
 }
 }
 }