İlkay İlknur

just a developer...

Object,Collection ve Dictionary Initializers

Merhaba Arkadaşlar,

Bu yazımızda sizlerle C# 3.0 sürümünden itibaren bizlerle olan ve C# tarafında object’lerin ve collection’ların yaratılması sırasında developerlara oldukça kolaylık sağlayan Object & Collection & Dictionary Initiliazers konusunu inceliyor olacağız.

İlk olarak Object Initializers tarafına bakarsak Object Initializer yapısı basit olarak biz developerların özellikle object yaratımı sırasında objectlerin propertylerine ilk değerleri atamamız konusunda syntax bakımından oldukça kolaylık sağlamakta. Bunun yanında Object Initiliazers özellikle LINQ sorgularında ve anonymous type yaratımında da hayati önem arzetmekte.

Şimdi öncelikle basit bir sınıf tanımlayalım ve daha sonra Object Initializer mekanizmasıyla yaratmış olduğumuz object’in propertylerini ilk değerlerine set edelim.

class MyClass {         public int Property1 { get; set; } public string Property2 { get; set; } public MySecondClass Property3 { get; set; }}

class MySecondClass{ public int MyProperty { get; set; }}

 

Şimdi object initializer kullanarak MyClass tipinden bir nesne örneği yaratalım.

MyClass myClass = new MyClass(){ Property1 = 1, Property2 = "Property2", Property3 = new MySecondClass() { MyProperty = 3 }};

Gördüğünüz gibi oldukça basit bir şekilde nesnemizi yarattık. Peki arka planda neler oluyor ? Öyleyse hemen arka plana geçelim ve bakalım neler oluyor.

Farklılıklar aslında yaratılan IL tarafında gerçekleşmekte.

.method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 3 .locals init ( [0] class ConsoleApplication2.MyClass myClass, [1] class ConsoleApplication2.MyClass <>g__initLocal0, [2] class ConsoleApplication2.MySecondClass <>g__initLocal1) L_0000: nop L_0001: newobj instance void ConsoleApplication2.MyClass::.ctor()     L_0006: stloc.1 L_0007: ldloc.1 L_0008: ldc.i4.1 L_0009: callvirt instance void ConsoleApplication2.MyClass::set_Property1(int32) L_000e: nop L_000f: ldloc.1 L_0010: ldstr "Property2" L_0015: callvirt instance void ConsoleApplication2.MyClass::set_Property2(string)     L_001a: nop L_001b: ldloc.1 L_001c: newobj instance void ConsoleApplication2.MySecondClass::.ctor()     L_0021: stloc.2 L_0022: ldloc.2 L_0023: ldc.i4.3 L_0024: callvirt instance void ConsoleApplication2.MySecondClass::set_MyProperty(int32)     L_0029: nop L_002a: ldloc.2 L_002b: callvirt instance void ConsoleApplication2.MyClass::set_Property3(class ConsoleApplication2.MySecondClass)     L_0030: nop L_0031: ldloc.1 L_0032: stloc.0 L_0033: ret }

Akışa baktığımızda aslında bizim yaptığımız işlemleri compiler arka planda açarak, teker teker tüm işlemleri gerçekleştirmekte ve yaklaşık olarak aşağıdaki kod örneğine çevirmekte.

MyClass myClass = new MyClass();myClass.Property1 = 1;myClass.Property2 = "Property2";MySecondClass second = new MySecondClass();second.MyProperty = 3;myClass.Property3 = second;  

Şimdi yukarıdaki kod ile bir önceki object initializer kullanarak yazdığımız kodu karşılaştırdığımızda özellikle okunulabilirlik bakımından oldukça büyük farklar bulunmakta. İlk kullanımda kodu okuyan developer hemen hızlı bir şekilde set edilen değerleri okuyup geçerken diğer örnekte ise tek tek referansları kontrol etmek zorunda kalmakta ve daha çok zaman kaybetmekte. Bunun yanında kodu yazan developer da ikinci kullanımda birinci kullanıma göre daha çok zaman kaybetmekte.

İşte bu noktadaki problemi çözmek için compiler devreye girmekte ve developerlara oldukça kolay bir tanımlama syntax’ı sağlarken arka planda da bu syntax’ın kullanıldığı kısımları yeniden yazarak aslında arka planda yine klasik kullanımı muhafaza etmekte ;)

Collection Initializers

C# 3.0 ile beraber gelen bir diğer syntax kolaylığı ise Collection Initializers’dı. Collection Initializers ile yarattığımız koleksiyonlar içerisine elemanları hızlı bir şekilde yerleştirebilmekteyiz.

Örneğin, List<int> list = new List<int>(){ 1,2,3,4,5,6,7};  

yukarıdaki gibi bir kullanımla bir List<T> tipinden biri koleksiyon örneği yarattıktan sonra içerisine 1’den 7’ye kadar elemanları ekleyebilmekteyiz. Bu kullanım aslında C# 3.0 öncesinde array’ler için geçerliydi. Ancak C# 3.0 ile beraber bu kullanım kolaylığı collection tipleri için de getirildi.

Peki collection initializers kullandığımızda arka planda neler oluyor ? :) .method private hidebysig static void Main(string[] args) cil managed { .entrypoint .maxstack 2 .locals init ( [0] class [mscorlib]System.Collections.Generic.List`1<int32> list, [1] class [mscorlib]System.Collections.Generic.List`1<int32> <>g__initLocal0) L_0000: nop L_0001: newobj instance void [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()     L_0006: stloc.1 L_0007: ldloc.1 L_0008: ldc.i4.1 L_0009: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_000e: nop L_000f: ldloc.1 L_0010: ldc.i4.2 L_0011: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)     L_0016: nop L_0017: ldloc.1 L_0018: ldc.i4.3 L_0019: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_001e: nop L_001f: ldloc.1 L_0020: ldc.i4.4 L_0021: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_0026: nop L_0027: ldloc.1 L_0028: ldc.i4.5     L_0029: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_002e: nop L_002f: ldloc.1 L_0030: ldc.i4.6 L_0031: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_0036: nop L_0037: ldloc.1 L_0038: ldc.i4.7 L_0039: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)     L_003e: nop L_003f: ldloc.1 L_0040: stloc.0 L_0041: ret }

Burada da baktığımızda aslında yazdığımız kodun aksine compiler arka planda öncelikle bir List nesne örneği yaratmakta ve sonrasında da tüm elemanları List tipinin Add metodunu kullanarak eklemekte. Aslında bizim normalde de kullanmamız gereken bu metodu compiler arka planda kendisi kullanmış ve biz developerlara daha kısa bir syntax ile uygulama geliştirme imkanı sağlamış.

Collection Initializer mekanizmasını kullanmamız için sadece tek bir kısıt bulunmakta. O da tipimizin IEnumerable interface’ini implemente etmesi ve tip içerisinde Add metodunun bulunması. Burada aklımıza gelen bir soru bulunmakta. Bu da arka planda generate edilen IL kodlarında IEnumerable interface’inin kullanılmamasına rağmen collection initializers mekanizmasında neden zorunlu olarak IEnumerable interface’inin tip tarafından implemente edilmek zorunda olduğu. Bunun nedeni de aslında C# programlama dili tasarım ekibi tarafından alınmış bir karara dayanmakta. C# 3.0 ile ilgili araştırmalar yaparken collection Initializers yapısının destekleneceği noktalar ekip tarafından incelenmiş ve bu özellik collection tipleri ile sınırlı bırakılmış ve bu nedenle de tipin IEnumerable interface’ini implemente etmesi gerektiği zorunlu hale getirilmiştir.

Dictionary Initializers

Bu yazımızda son olarak bahsedeceğimiz konu da Dictionary Initializers. Collection Initializers ile collection tipleri içerise kolay bir şekilde eleman ekleyebilirken, aslında bu ihtiyaca benzer bir durum da dictionary tipleri için doğmakta. Bazı durumlarda yarattığımız Dictionary’ler içerisine elemanları teker teker eklememiz gerekebilmekte. Bu noktada da yine Collection Initializers’da olduğu gibi C# tarafından sağlanan kolay bir syntax ile dictionary içerisine ilk elemanları atabilmekteyiz.

Dictionary Initializers’ın kullanımı ise şu şekilde olmakta.

Dictionary<int, string> a = new Dictionary<int, string>(){ {1,"Bir"}, {2,"İki"}, {3,"Üç"}};

Peki compiler arka planda ne yapmakta ?

Dictionary<int, string> <>g__initLocal0 = new Dictionary<int, string>();

<>g__initLocal0.Add(1, "Bir");

<>g__initLocal0.Add(2, "İki");

<>g__initLocal0.Add(3, "Uc");

Dictionary<int, string> a = <>g__initLocal0;

 

Compiler kimi zaman bu yazımızda da gördüğümüz gibi bir takım kolay syntax yapılarını developerlara sunarken arka planda aslında bu syntax yapısını klasik kullanımlara geri çevirerek developerların yazılım geliştirme sürelerini kısaltmayı amaçlamakta. Kimi zaman da keywordler veya modifierlar sunarak arka planda kurduğu generic yapılarla çok daha farklı işleri biz developerlara hissettirmeden gerçekleştirmekte. ;)

Hoşçakalın



Yorum Gönder