30 Ekim 2013 Çarşamba

C# ve Constructorlar -I

Instance Constructrolar


Constructor’lar objeleri initialize ettiğimizde devreye giren özel metodlardır. Eğer obje class’ı içerisinde bir constructor tanımlı değilse bile compiler tarafından otomatik olarak boş bir constructor sağlanır.

         Bir objenin yaratılması işlemi iki aşamada gerçekleşir:

 Birinci aşamada obje için memory’de alokasyon gerçekleştirilir. Her obje new operator’ü kullanarak yaratılır. Bu kural için herhangi bir exception yoktur. Bunu explicit olarak kod içerisinde gerçekleştirebiliriz, yada compiler bunu otomatik olarak yerine getirir.

         int[] MyIntArray={1,2,3,4};
         int[] MyIntArray1=new int[4]{1,2,3,4};

         Yukarıdaki deklarasyonlar’ın her ikiside aynı işi yapmaktadır.

         Bir objenin yaratılmasındaki ikinci aşamada ise constructor çağrımıdır. Constructor new ile allocate edilen memory’i obje’ye çevirir. İki tip constructor’ın varlığından bahsedebiliriz: static ve instance. Instance constructor’lar objelerin initialize’ı için kullanırlırlar. Static constructor’lar ise class’ların initialize’ı için kullanılırlar. C++’da initialize işlemi gerçekleştirmeden new operator’ü memory allokasyonu ve yine new keyword’ü ile daha önceden allocate edilmiş memory’i initalize etmek mümkündür. Bu ayrım C#’da mümkün değildir. Bu yöntem ile C# memory’deki yapı okunmadan önce doğru değere atanma yapılmasını garanti etmiş olur.

          Default constructor class ile aynı isimdedir, geriye bir değer döndürmez ve bir metod değildir (void). Default constructor’ın herhangi bir parametresi yoktur. İstenildiği takdir de parametrik constructor’lar tanımlanılabilir.

Constructor aynı zamanda implicit olarak static olmayan alanları initialize işlemine tabi tutar. int, double, decimal gibi nümerik alanlar 0’a, bool alanlar false’a Referans tipler null’a, Struct’ların içerdiği alanların hepsi yukarıdaki yapıya uygun olarak initialize işlemine tabi tutulurlar. Default constructor’ın bu işlmeleri yapmasının iyi yanlarının yanı sıra  dezavantajlı yanlarıda vardır: Mesela bir tarih class’ın da yıl, ay ve gün değerlerinin 0’dan başlamasını istemeyiz. Böyle durumda kendi constructor’ımızı tanımlamamız gerekmektedir.

         Aşağıdaki örnekte default constructor kullanan bir class ve kullanımı görülmektedir.




using System;
class Tarih
{
         public short Gun,Ay,Yil;
}
class Class1
{
                   [STAThread]
                   static void Main(string[] args)
                   {
                            Tarih a=new Tarih();
                            Console.WriteLine("Yıl:{0}",a.Yil);
                            Console.WriteLine("Ay:{0}",a.Ay);
                            Console.WriteLine("gün:{0}",a.Gun);
                            Console.ReadLine();
                   }
}       

         Bu class’ı test ettiğimizde Ay, Gun ve Yıl değerlerinin 0’a set edildiği rahatlıkla görülmektedir. Default constructor’ı devre dışı bırakıp kendi constructor’ımızı yazmak istersek kodumuz aşağıdaki şekilde olacaktır:

using System;
class Tarih
{
         public short Gun,Ay,Yil;
         public Tarih()
         {
                   Gun=1;
                   Ay=1;
                   Yil=1970;
         }
}
class Class1
{
                   [STAThread]
                   static void Main(string[] args)
                   {
                            Tarih a=new Tarih();
                            Console.WriteLine("Yıl:{0}",a.Yil);
                            Console.WriteLine("Ay:{0}",a.Ay);
                            Console.WriteLine("gün:{0}",a.Gun);
                            Console.ReadLine();
                   }
}


         Bu kod çalıştığında ise 1.1.1970 tarihi ile karşılaşırız. Default constructor ve kendi oluşturduğumuz constructor arasındaki fark açık bir şekilde görülmektedir.

Constructor’lar tıpkı metod’larda olduğu gibi overload edilebilirler. Bu sayede class’ımızın farklı initialization işlemlerine tabi tutulması sağlanılabilir. Overload etme işlemi gayet basittir. Class ile aynı isimde ve diğer constructor’dan farklı parametreler kabul eden bir tanım vererek bunu sağlayabiliriz. Az önce gördüğümüz örnekteki constructor’ı overload edecek olursak:



using System;
class Tarih
{
         public short Gun,Ay,Yil;
         public Tarih()
         {
                   Gun=1;
                   Ay=1;
                   Yil=1970;
         }
         public Tarih(short Day,short Month,short Year)
         {
                   Gun=Day;
                   Ay=Month;
                   Yil=Year;
         }
}
class Class1
{
                   [STAThread]
                   static void Main(string[] args)
                   {
                            Tarih a=new Tarih();
                            Tarih b=new Tarih(2,2,2002);
                            Console.WriteLine("a.Yıl:{0}",a.Yil);
                            Console.WriteLine("a.Ay:{0}",a.Ay);
                            Console.WriteLine("a.Gün:{0}",a.Gun);
                            Console.WriteLine("b.Yıl:{0}",b.Yil);
                            Console.WriteLine("b.Ay:{0}",b.Ay);
                            Console.WriteLine("b.Gün:{0}",b.Gun);
                            Console.ReadLine();
                  }
}

         Görüldüğü gibi a ile ilgili olan tarih 1.1.1970 iken b ile ilgili olan tarih parametrik olarak ikinci constructor’a yönlendiği için 2.2.2002 tarihini elde etmiş oluruz. Dikkat ettiyseniz birinci constructor içerisinde de ikincisine benzer bir işlem yaptık; basit bir assignment işlemi. Her seferinde aynı kodu yazmaktansa diğer bir yöntem ile işimizi biraz daha kolaylaştırabiliriz birinci constructor çağrıldığında onu ikinci constructor’a yönlendirebiliriz:

using System;
class Tarih
{
         public short Gun,Ay,Yil;
         public Tarih():this(1,1,1970)
         {
         }
         public Tarih(short Day,short Month,short Year)
         {
                   Gun=Day;
                   Ay=Month;
                   Yil=Year;
         }
}
class Class1
{
                   [STAThread]
                   static void Main(string[] args)
                   {
                            Tarih a=new Tarih();
                            Tarih b=new Tarih(2,2,2002);
                            Console.WriteLine("a.Yıl:{0}",a.Yil);
                            Console.WriteLine("a.Ay:{0}",a.Ay);
                            Console.WriteLine("a.Gün:{0}",a.Gun);
                            Console.WriteLine("b.Yıl:{0}",b.Yil);
                            Console.WriteLine("b.Ay:{0}",b.Ay);
                            Console.WriteLine("b.Gün:{0}",b.Gun);
                            Console.ReadLine();
                   }
}

         Birinci constructor’da yaptığımız :this(1,1,1970) yönlendirmesi ile üç parametre kabul eden ikinci constructor’ı çağırmış olduk. Bu yönteme initializer list adı verilmektedir. Bu noktada bilmemiz gereken bir kural ise bir initializer list içerisinde tanımlı metod’un kendi kendini çağıramamasıdır.
 
         Class içerisinde bazen readonly değişkenler kullanmak istiyebiliriz. Yani değeri bir kere assign edildikten sonra kullanıcı tarafından değiştirilmesini istemediğimiz değişkenler. Bu şekildeki değişkenler class içerisinde aşağıdaki şekilde tanımlanılabilirler:

         public readonly int MinYil=0;

         Bu tanımlama ile sonradan değiştirilemeyecek bir değer ataması yapmış oluruz. Fakat bazı durumlarda bu değerin class’ın yaratımı içerisinde değişmesini isteyebiliriz. Mesela class içerisinde bir array’imiz var ve initialization sırasında bu array’in Max range’inin belirlenmesini istiyoruz. Bu durumda bir şekilde kullanıcıya readonly olan bu değere bir kereye mahsus bir assignment şansı vermek zorundayız. Bu gibi durumlarda ise constructor’ları kullanmak zorunda kalırız.

Bir örnekle bakacak olursak:

using System;
class MyArray
{
         public readonly int MaxRangeOfArray;
         public int[] MyArr;
         public MyArray(int MaxRange)
         {
                   MaxRangeOfArray=MaxRange;
         }
         public void CreateArray()
         {
                   MyArr=new int[MaxRangeOfArray];
         }
}
class Class1
{
                   [STAThread]
                   static void Main(string[] args)
                   {
                            MyArray a=new MyArray(1);
                            a.CreateArray();
                            a.MyArr[1]=2;
                            Console.ReadLine();
                   }
}

         MaxRangeOfArray değişkenine yapmış olduğumuz atama sadece constructor içerisinde gerçekleşebilir. Constructor dışında herhangi bir metod içerisinde bu değişkene yapılacak atama compiler’ın hata vermesine neden olacaktır.


Hiç yorum yok:

Yorum Gönder