C Sharp Programlama Dili/Sınıflar

Vikikitap, özgür kütüphane
Ders 11. Sınıflar


Sınıflar nesne yönelimli programlamanın en önemli ögesidir. Sınıflar sayesinde programlarımızı parçalara bölüp karmaşıklığını azaltırız.

C#'ta metotlar ve özellikler mutlaka bir sınıfın içinde olmalıdır. Metotlar bir veya daha fazla komutun bir araya getirilmiş halidir; parametre alabilirler, geriye değer döndürebilirler. Özellikler ise bellek gözeneklerinin programlamadaki karşılıklarıdır. Bu bakımdan özellikler değişkenlere benzerler. Aradaki en temel fark değişkenlerin bir metot içinde tanımlanıp yalnızca tanımlandığı metot içinde etkinlik gösterebilmesine rağmen özelliklerin tıpkı metotlar gibi bir üye eleman olmasıdır. Bu bakımdan özelliklerin tuttuğu değerlere daha fazla yerden erişilebilir.

Sınıf Oluşturmak[değiştir]

Üç özelliği ve iki metodu olan örnek bir sınıf oluşturulması:

 class SinifIsmi
 {
    public int ozellik1;
    private string ozellik2;
    float ozellik3;
    public int metot1(int a,int b)
    {
       return a + b;
    }
    private void metot2(string a)
    {
       Console.WriteLine(a);
    }
 }

Burada dikkatinizi public ve private anahtar sözcükleri çekmiş olmalı. Bir metot ya da özelliğe bulunduğu sınıfın dışından da erişilebilmesini istiyorsak public sözcüğü kullanılır. private sözcüğü kullanmakla hiçbir şey yazmamak aynı etkiye sahiptir ve iki durumda da metot ya da özelliğe yalnızca bulunduğu sınıfın içinden erişilebilir. Bu sınıftaki yalnızca ozellik1 özelliğine ve metot1 metoduna SinifIsmi sınıfının dışından erişilebilir. Diğer özellik ve metotlara erişilemez. Şimdi bu sınıfı programımızda kullanalım:

 using System;
 class SinifIsmi
 {
    public int ozellik1;
    public string ozellik2;
    public float ozellik3;
    public int metot1(int a,int b)
    {
       return a+b;
    }
    public void metot2(string a)
    {
       Console.WriteLine(a);
    }
 }
 class EsasSinif
 {
    static void Main()
    {
       SinifIsmi nesne=new SinifIsmi();
       Console.WriteLine(nesne.ozellik1);
       Console.WriteLine(nesne.ozellik2);
       Console.WriteLine(nesne.ozellik3);
       Console.WriteLine(nesne.metot1(2,5));
       nesne.metot2("deneme");      
    }
 }

Bu programda SinifIsmi sınıfındaki bütün özellik ve metotların public anahtar sözcüğü ile belirtildiğine dikkat edin. Oluşturduğumuznesne nesnesiyle sınıfın bütün özellik ve metotlarına eriştik. ozellik1, ozellik2 ve ozellik3 özelliklerine hiçbir değer atanmamış, ancak programımız hata vermedi, sadece derleme sonunda uyarı verdi. Çünkü SinifIsmi nesne=new SinifIsmi(); satırındaki new anahtar sözcüğü sayesinde sınıftaki bütün özellikler nesne nesnesi için türlerine göre varsayılan değere atandı. Eğer SinifIsmi nesne=new SinifIsmi(); yerine SinifIsmi nesne; yazsaydık programımız hata verirdi. Ayrıca programımızda metot2 metodu değer tutan değil, iş yapan bir metot, o yüzden tek başına kullanıldı. Başka bir örnek:

 using System;
 class SinifIsmi
 {
    public int ozellik1=55;
    public string ozellik2="deneme";
    public float ozellik3=123.78f;
    public int metot1(int a,int b)
    {
       return a+b;
    }
    public void metot2(string a)
    {
       Console.WriteLine(a);
    }
 }
 class EsasSinif
 {
    static void Main()
    {
       SinifIsmi nesne=new SinifIsmi();
       Console.WriteLine(nesne.ozellik1);
       Console.WriteLine(nesne.ozellik2);
       Console.WriteLine(nesne.ozellik3);
       Console.WriteLine(nesne.metot1(2,5));
       nesne.metot2("Vikikitap");      
    }
 }

Bu programda ise özelliklere önce değer verdik ve esas programda da bu değerler ekrana yazıldı.

NOT: Tipi bir dizi olan bir özelliğe ilk değer verirken kısa dizi notasyonunu kullanabiliriz (değişkenlerde yaptığımız gibi). Örnek:

using System;
class Deneme
{
    public int[] ozellik={1,2,3};
}
class EsasSinif
{
    static void Main()
    {
        Deneme d=new Deneme();
        Console.WriteLine(d.ozellik[0]);
    }
}

Bu şekildeki kısa dizi notasyonu sadece değişkenlere ve özelliklere tanımlandığı satırda ilk değer verirken kullanılabilir.

Aynı sınıf türünden birden fazla nesne oluşturma[değiştir]

Tahmin edebileceğiniz gibi C#'ta aynı sınıf türünden birden fazla nesne oluşturulabilir ve bu nesnelerle sınıfın özellik ve metotlarına erişilebilir. Örnek:

 using System;
 class KrediHesabi
 {
    public ulong HesapNo;
 }
 class AnaSinif
 {
    static void Main()
    {
       KrediHesabi hesap1=new KrediHesabi();
       KrediHesabi hesap2=new KrediHesabi();
       hesap1.HesapNo=3456;
       hesap2.HesapNo=1111;
       Console.WriteLine(hesap1.HesapNo);
       Console.WriteLine(hesap2.HesapNo);
    }
 }

Burada KrediHesabi sınıfı türünden hesap1 ve hesap2 adlı iki nesne tanımlandı ve bu nesnelerle KrediHesabi sınıfının HesapNo özelliğine erişildi. Burada bilmemiz gereken şey farklı nesnelerle bir sınıfın özelliğine erişip özelliğin değerini değiştirdiğimizde aslında birbirinden farklı değişkenlerin değerini değiştirmiş olmamızdır. Bu program ekrana alt alta 3456 ve 1111 yazacaktır. Çünkü KrediHesabi hesap1=new KrediHesabi(); ve KrediHesabi hesap2=new KrediHesabi(); satırlarıyla birbirinden farklı iki nesne oluşturduk ve bu iki nesne için bellekte ayrı ayrı alan tahsisatı yaptık. Eğer programı şöyle değiştirirsek,

 using System;
 class KrediHesabi
 {
    public ulong HesapNo;
 }
 class AnaSinif
 {
    static void Main()
    {
       KrediHesabi hesap1=new KrediHesabi();
       KrediHesabi hesap2=hesap1;
       hesap1.HesapNo=3456;
       hesap2.HesapNo=1111;
       Console.WriteLine(hesap1.HesapNo);
       Console.WriteLine(hesap2.HesapNo);
    }
 }

Bu durumda alt alta 1111 ve 1111 yazılacaktır. Çünkü KrediHesabi hesap2=hesap1; satırıyla KrediHesabi sınıfı türünden yeni bir nesne oluşturduk ancak bu nesne için bellekte ayrı bir alan tahsisatı yapmadık. Nesneyi hesap1 nesnesinin bellekteki adresine yönlendirdik. Dolayısıyla hesap1'in özelliğinde yapılan bir değişiklik hesap2'yi, hesap2'nin özelliğinde yapılan bir değişiklik de hesap1'i etkileyecektir. Başka bir örnek program:

 using System;
 class EsasSinif
 {
    int a;
    static void Main()
    {
       EsasSinif nesne=new EsasSinif();
       Console.WriteLine(nesne.a);      
    }
 }

Gördüğünüz gibi özellik ve metot tanımlamaları için ayrı bir sınıf oluşturmak zorunda değiliz. Main() metodunun olduğu sınıfa istediğimiz üye elemanları ekleyebiliriz. Bu durumda da özelliğine erişebilmek için a özelliğini public olarak belirtme zorunluluğundan da kurtulmuş olduk.

NOT: Farkındaysanız şimdiye kadar hiçbir metot ya da özelliğimizi static anahtar sözcüğüyle belirtmedik. static anahtar sözcüğüyle belirttiğimiz metot ya da özellikleri direkt olarak söz konusu metot ya da özelliğin adını yazarak kullanabilriz. Örnek:

 using System;
 class EsasSinif
 {
    static int a;
    static void Main()
    {
       Console.WriteLine(a);      
    }
 }

Bu program ekrana 0 yazacaktır. Eğer a'ya bir değer verseydik o değer yazacaktı.

NOT: Diziler tıpkı değişkenler gibi birbirine atanabilirler. Örnek:

 int[] a={1,6,7,12};
 int[] b=a;

Burada b dizisine a dizisi adres gösterme yoluyla aktarılmıştır, dolayısıyla da a dizisinin bir elemanında yapılan bir değişiklik b dizisini, b dizisinin bir elemanında yapılan bir değişiklik a dizisini etkileyecektir. Çünkü diziler birbirlerine adres gösterme yoluyla atanır. Ayrıca aşağıdaki gibi bir kullanım da doğrudur:

 int[] a={1,6,7,12};
 int[] b={1,5};
 b=a;

Burada b dizisi sahip olduğu elemanları kaybedip a dizisinin adresine yönlendi. Bu ve benzer şekilde diziler birbirlerine atanabilir. Yani aynı adrese yönlendirilebilirler.

Örnekler[değiştir]

Bu program bir metoda girilen iki parametreyi ve bunların çarpımını ekrana yazar. Eğer bir dikdörtgen söz konusu olduğunu düşünürsek dikdörtgenin enini, boyunu ve alanını ekrana yazar.

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    public int Alan()
    {
       int Alan=En*Boy;
       return Alan;
    }
    public void EnBoyBelirle(int en,int boy)
    {
       En=en;
       Boy=boy;
    }
    public void Yaz()
    {
       Console.WriteLine("***************");
       Console.WriteLine("En:{0,5}",En);
       Console.WriteLine("Boy:{0,5}",Boy);
       Console.WriteLine("Alan:{0,5}",Alan());
       Console.WriteLine("***************");
    }
 }
 class AnaSinif
 {
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       d1.Yaz();
       Dortgen d2=new Dortgen();
       d2.EnBoyBelirle(25,12);
       d2.Yaz();
    }
 }

Bu program Main blokundan çalışmaya başlar. Önce Dortgen sınıfı türünden d1 nesnesi oluşturulur. Bu nesneyle Dortgen sınıfındaki EnBoyBelirle metodu çalıştırılır. Bu metot geçerli nesne için En ve Boy özelliğini metoda girilen parametreler yapar. Sonra aynı nesneyle Yaz metodu çalıştırılıyor. Yaz metodu da geçerli nesnenin özelliklerini ve Alan metodunun sonucunu ekrana yazdırıyor. Sonra aynı olaylar d2 nesnesi için de gerçekleşiyor. Burada önemli olan bir sınıf türünden oluşturduğumuz her bir nesne için bellekte o sınıftaki tüm metot ve özellikler için bir yer açılması ve tüm işlemlerin o bellek bölgesinde yapılması olayını kavramamız. Ayrıca gördüğünüz gibi static olmayan metotların gövdesinde aynı sınıftaki özellik ve metotlar static olsun olmasın direkt olarak kullanılabiliyor. Ancak programı şöyle değiştirseydik hata çıkacaktı.

 using System;
 class Dortgen
 {
    public int En=20;
    public int Boy=5;
    public int Alan()
    {
       int Alan=En*Boy;
       return Alan;
    }
    static void Main()
    {
       Console.WriteLine("***************");
       Console.WriteLine("En:{0,5}",En);
       Console.WriteLine("Boy:{0,5}",Boy);
       Console.WriteLine("Alan:{0,5}",Alan());
       Console.WriteLine("***************");
    }
 }

Çünkü burada En ve Boy özellikleriyle Alan() metodu tek başına kullanılamaz. Önce Dortgen sınıfı türünden bir nesne oluşturup bu nesne üzerinden bu özellik ve metotlara erişilmelidir. Çünkü Main metodu static bir metottur. Başka bir örnek:

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    public int Alan()
    {
       int Alan=En*Boy;
       return Alan;
    }
    public void EnBoyBelirle(int en,int boy)
    {
       En=en;
       Boy=boy;
    }
    public void Yaz()
    {
       Console.WriteLine("***************");
       Console.WriteLine("En:{0,5}",En);
       Console.WriteLine("Boy:{0,5}",Boy);
       Console.WriteLine("Alan:{0,5}",Alan());
       Console.WriteLine("***************");
    }
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       d1.Yaz();
       Dortgen d2=new Dortgen();
       d2.EnBoyBelirle(25,12);
       d2.Yaz();
   }
 }

Bu program da geçerlidir. Başka bir örnek:

 using System;
 class Dortgen
 {
    public int En=30;
    public int Boy=23;
    public int Alan()
    {
       int Alan=En*Boy;
       return Alan;
    }
    static void Main()
    {
       yaz d1=new yaz();
       d1.Yaz();
   }
 }
 class yaz
 {
     public void Yaz()
    {
       Console.WriteLine("***************");
       Console.WriteLine("En:{0,5}",Dortgen.En);
       Console.WriteLine("Boy:{0,5}",Dortgen.Boy);
       Console.WriteLine("Alan:{0,5}",Dortgen.Alan());
       Console.WriteLine("***************");
    }
 }

Bu program çalışmaz. Çünkü, static olmayan metotların gövdesinde sadece aynı sınıftaki özellik ve metotlar static olsun olmasın direkt olarak kullanılabilir.

Şimdi evimizin üyelerinin (annemizin, babamızın vs.) özelliklerini gireceğimiz, yaşlarını hesaplayacağımız daha zevkli bir örnek yapalım. Yani evimizi bir sınıfmış gibi düşünelim.

 using System;
 class EvHalki
 {
    public int DogumYili;
    public string Meslek;
    public string Ad;
    public string GozRengi;
    public int Yas()
    {
       return 2008-DogumYili;
    }
    public void OzellikleriYaz()
    {
       Console.WriteLine("---------------");
       Console.WriteLine("Adı: "+Ad);
       Console.WriteLine("Yaşı: "+Yas());
       Console.WriteLine("Mesleği: "+Meslek);
       Console.WriteLine("Göz rengi: "+GozRengi);
       Console.WriteLine("---------------");
    } 
 }
 class AnaProgram
 {
    static void Main()
    {
       EvHalki annem=new EvHalki(); //EvHalki sınıfı türünden yeni bir nesne oluşturuldu.
       annem.DogumYili=1964; //Oluşturulan nesnenin bir özelliği girildi.
       annem.Meslek="Ev hanımı";
       annem.Ad="Hatice";
       annem.GozRengi="Mavi";
       EvHalki babam=new EvHalki();
       babam.DogumYili=1950;
       babam.Meslek="Emekli";
       babam.Ad="Kenan";
       babam.GozRengi="Kahverengi";
       EvHalki kardesim=new EvHalki();
       kardesim.DogumYili=1987;
       kardesim.Meslek="Öğrenci";
       kardesim.Ad="Uğur";
       kardesim.GozRengi="Yeşil";
       annem.OzellikleriYaz();
       babam.OzellikleriYaz();
       kardesim.OzellikleriYaz();
    }
 }

Şimdi bu programda ufak bir oynama yapalım:

 using System;
 class EvHalki
 {
    public int DogumYili;
    public string Meslek;
    public string Ad;
    public string GozRengi;
    public int Yas()
    {
       return 2008-DogumYili;
    }
    public void OzellikleriYaz()
    {
       Console.WriteLine("---------------");
       Console.WriteLine("Adı: "+Ad);
       Console.WriteLine("Yaşı: "+Yas());
       Console.WriteLine("Mesleği: "+Meslek);
       Console.WriteLine("Göz rengi: "+GozRengi);
       Console.WriteLine("---------------");
    } 
 }
 class AnaProgram
 {
    static void Main()
    {
       EvHalki annem=new EvHalki();
       annem.DogumYili=1964;
       annem.Meslek="Ev hanımı";
       annem.Ad="Hatice";
       annem.GozRengi="Mavi";
       EvHalki babam=new EvHalki();
       babam.OzellikleriYaz();
    }
 }

Gördüğünüz gibi annemin özellikleri girilmesine rağmen OzellikleriYaz metodu çağrılmadığı için özellikler ekrana yazılmadı. Ayrıca da babama da herhangi bir özellik girilmemesine rağmen OzellikleriYaz metoduyla ekrana yazdırdık. Ekrana bütün özelliklerin varsayılanları yazıldı. Çünkü EvHalki babam=new EvHalki(); satırındaki altı çizili kısım sayesinde bellekte babam nesnesi için ayrı bir yer ayrıldı ve bütün özellikler varsayılan değere atandı. Yalnızca EvHalki babam; satırı olsaydı Evhalki sınıfı türünden babam nesnesi oluşturulurdu, ancak bellekte bu nesne için ayrı bir yer ayrılmazdı.

this anahtar sözcüğü[değiştir]

Şimdi şöyle bir program yazalım:

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    void EnBoyBelirle(int en,int boy)
    {
       En=en;
       Boy=boy;
    }
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       Console.WriteLine(d1.En+"\n"+d1.Boy);
    }
 }

Tahmin edebileceğiniz bu programda önce d1 nesnesinin En ve Boy özelliğini sırasıyla 20 ve 50 olarak ayarlıyoruz, sonra da bu d1 nesnesinin özelliklerini ekrana yazdırıyoruz. Bu programı şöyle de yazabilirdik:

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    void EnBoyBelirle(int en,int boy)
    {
       this.En=en;
       this.Boy=boy;
    }
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       Console.WriteLine(d1.En+"\n"+d1.Boy);
    }
 }

Buradaki this anahtar sözcüklerinin Türkçe karşılığı "beni çağıran nesnenin"dir. Peki ama zaten this'i yazmasak da aynı şey olurdu. Şimdi başka bir örnek yapalım:

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    void EnBoyBelirle(int En,int Boy)
    {
       En=En;
       Boy=Boy;
    }
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       Console.WriteLine(d1.En+"\n"+d1.Boy);
    }
 }

C# bu şekilde bir kullanıma izin verir. Çünkü Main blokunda EnBoyBelirle metodunu çalıştırdığımızda sadece EnBoyBelirle metodu çalışır, dolayısıyla da derleyici bu metottan önce tanımlanmış olan En ve Boy özelliklerini görmez. Burada alınan parametreler En ve Boy değişkenlerine atanıyor. Sonra En=En; ve Boy=Boy; satırlarıyla da aslında alınan parametreler yine aynı değişkenlere atanıyor. Tahmin edebileceğiniz gibi aslında burada bizim yapmak istediğiniz aldığı parametreleri d1 nesnesinin En ve Boy özelliklerine aktarmak. İşte bunun için this anahtar sözcüğünü kullanılırız:

 using System;
 class Dortgen
 {
    public int En;
    public int Boy;
    void EnBoyBelirle(int En,int Boy)
    {
       this.En=En;
       this.Boy=Boy;
    }
    static void Main()
    {
       Dortgen d1=new Dortgen();
       d1.EnBoyBelirle(20,50);
       Console.WriteLine(d1.En+"\n"+d1.Boy);
    }
 }

Bu programda artık EnBoyBelirle metodunun aldığı parametreler d1 nesnesinin özelliklerine atanacaktır. Benzer şekilde C# aşağıdaki gibi bir kullanıma da izin verir.

using System;
 class Dortgen
 {
    int En;
    int Boy;
    static void Main()
    {
       int En=50;
       int Boy=100;
       Console.WriteLine(En+"\n"+Boy);
    }
 }

Eğer En ve Boy değişkenleri Main blokunun içinde tanımlanmasaydı programımız hata verecekti. Başka bir örnek:

 using System;
 class Dortgen
 {
    static int En=8;
    static int Boy=3;
    static void Main()
    {
       int En=50;
       int Boy=100;
       Console.WriteLine(En+"\n"+Boy);
    }
 }

Burada ekrana sırasıyla 50 ve 100 yazılacaktır. Ancak;

 using System;
 class Dortgen
 {
    static int En=8;
    static int Boy=3;
    static void Main()
    {
       int Boy=100;
       Console.WriteLine(En+"\n"+Boy);
    }
 }

Bu sefer de ekrana sırasıyla 8 ve 100 yazıldı. Çünkü derleyici En değişkenini Main bloku içinde bulamayınca sınıfın bir özelliği olan En'i ekrana yazdı. En ve Boy özellikleri static olarak tanımlandığı için bu özelliklere nesne oluşturmaya gerek kalmadan direkt erişebildik.

get ve set anahtar sözcükleri[değiştir]

Şimdi aşağıdaki küçük programı yazalım:

 using System;
 class YardimciSinif
 {
    int Sayi;
    public void SayiBelirle(int sayi)
    {
       Sayi=sayi;
    }
    public int SayiAl()
    {
       return Sayi;
    }
 }
 class AnaSinif
 {
    static void Main()
    {
       YardimciSinif nesne=new YardimciSinif();
       nesne.SayiBelirle(34);
       Console.WriteLine(nesne.SayiAl());
    }
 }

Bu program oldukça basit. YardimciSinif sınıfının iki tane metodu ve bir tane de özelliği var. Ancak özellik private olduğu için bu özelliğe başka bir sınıftan erişmek veya değiştirmek mümkün değil. Bu yüzden bu özelliği değiştirmek veya bu özelliğe erişmek istediğimizde public olan SayiBelirle ve SayiAl metotlarını kullandık. Peki bunun bize ne faydası var? Aslında Sayi özelliğini public olarak ayarlayıp direkt olarak özellik üzerinde işlem yapılabilirdi. İşte faydası:

 using System;
 class YardimciSinif
 {
    int Sayi;
    public void SayiBelirle(int sayi)
    {
       if(sayi<0)
          Sayi=0;
       else
          Sayi=sayi;
    }
    public int SayiyiAl()
    {
       if(Sayi>100)
          return Sayi/100;
       else
          return Sayi;
    }
 }
 class AnaSinif
 {
    static void Main()
    {
       YardimciSinif nesne=new YardimciSinif();
       nesne.SayiBelirle(34);
       Console.WriteLine(nesne.SayiyiAl());
    }
 }

Gördüğünüz gibi özelliğe değer atamayı ve özelliğin değerini ekrana yazdırmayı metotlar sayesinde koşullandırabildik. Eğer direkt özellik üzerinde işlem yapmaya kalkışsaydık böyle bir şansımız olmazdı. Şimdi benzer bir durumu set ve get sözcükleriyle oluşturalım:

 using System;
 class YardimciSinif
 {
    int Sayi;
    public int SahteOzellik
    {
       set
       {
          if(value<0)
             Sayi=0;
          else
             Sayi=value;
       }
       get
       {
          if(Sayi>100)
             return Sayi/100;
          else
             return Sayi;
       }
    }
 }
 class AnaSinif
 {
    static void Main()
    {
       YardimciSinif nesne=new YardimciSinif();
       nesne.SahteOzellik=110;
       Console.WriteLine(nesne.SahteOzellik);
    }
 }

Gördüğünüz gibi önce YardimciSinif sınıfında SahteOzellik adlı bir özellik oluşturduk. Bu özellik gerçekten de sahtedir. Özelliğe bir değer atanmaya çalışıldığında set blokundaki, özellik kullanılmaya çalışıldığında da get blokundaki komutlar çalıştırılır. Aslında C# kütüphanesindeki özelliklerin çoğu bu yöntemle oluşturulmuştur. Örneğin ileride göreceğimiz Windows programlamada bir buton nesnesinin Text özelliğini değiştirdiğimizde butonun üzerindeki yazı değişir. Halbuki klasik özelliklere değer atama yönteminde sadece özelliğin değeri değişirdi. Bu yöntemde ise bir özelliğe değer atadığımızda çalışacak komutlar yazabiliyoruz. Unutmadan söyleyeyim; programdaki value sözcüğü özelliğe girilen değeri tutar. Özelliğe girilen değer hangi türdeyse o türde tutar. Ayrıca bu oluşturulan SahteOzellik özelliğinin metotlara oldukça benzediğini de dikkatinizi çekmiş olmalı.

NOT: Aslında tanımladığımız her dizi Array sınıfı türünden bir nesnedir. İşte bu yüzden tanımladığımız dizilerle Array sınıfının metot ve özelliklerine erişebiliriz. Örneğin Length Array sınıfı türünden bir özelliktir ve dizinin eleman sayısını verir. DiziAdi.Length yazılarak bu özelliğe erişilebilir.

NOT: Metot ve özelliklerin geri dönüş tipi bir dizi olabilir. Örnek:

 using System;
 class YardimciSinif
 {
    public int[] Dizi={7,4,3};
    public int[] Metot()
    {
       int[] a={23,45,67};
       return a;
    }
 }
 class AnaSinif
 {
    static void Main()
    {
       YardimciSinif nesne=new YardimciSinif();
       Console.WriteLine(nesne.Dizi[0]);
       Console.WriteLine(nesne.Metot()[2]);
    }
 }

Şimdi isterseniz bir dizinin türünü istenilen türe dönüştürmeye yarayan bir sınıf yazalım. Yani sınıftaki metotlar dizinin bütün elemanlarını istenilen türe dönüştürüp bu oluşturulan yeni diziyi tutacak. C#'ta diziler arasında bilinçsiz tür dönüşümü mümkün olmadığı gibi, bu işi yapacak metot da yok. Yani yapacağımız metotlar oldukça faydalı olacaktır.

 using System;
 class Donustur
 {
    public static int[] Inte(Array dizi)
    {
       int[] gecici=new int[dizi.Length];
       for(int i=0;i<dizi.Length;i++)
          gecici[i]=Convert.ToInt32(dizi.GetValue(i));
       return gecici;
    }
    public static string[] Stringe(Array dizi)
    { 
       string[] gecici=new string[dizi.Length];
       for(int i=0;i<dizi.Length;i++)
          gecici[i]=dizi.GetValue(i).ToString();
       return gecici;
    }
 }

Sınıfımız bu şekilde. Ancak sınıfımızın yalnızca inte ve stringe dönüştüm yapan iki metodu var. İsterseniz dönüşüm yapılabilecek tür sayısını yeni metotlar ekleyerek artırabilirsiniz. Şimdi bu sınıfı bir program içinde kullanalım.

 using System;
 class Donustur
 {
    public static int[] Inte(Array dizi)
    {
       int[] gecici=new int[dizi.Length];
       for(int i=0;i<dizi.Length;i++)
          gecici[i]=Convert.ToInt32(dizi.GetValue(i));
       return gecici;
    }
    public static string[] Stringe(Array dizi)
    { 
       string[] gecici=new string[dizi.Length];
       for(int i=0;i<dizi.Length;i++)
          gecici[i]=dizi.GetValue(i).ToString();
       return gecici;
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       string[] a={"2","5","7","9"};
       int[] b=Donustur.Inte(a);
       Console.WriteLine(b[1]+b[3]);
       int[] c={2,7,9,4};
       string[] d=Donustur.Stringe(c);
       Console.WriteLine(d[0]+d[3]);
    }
 }

HATIRLATMA: Gördüğünüz gibi metotların parametresindeki dizi, Array DiziAdi yöntemiyle oluşturulduğu için bu dizinin elemanlarına klasik indeksleme yöntemiyle erişemeyiz. Bu yüzden bu dizinin elemanlarına ulaşmak için Array sınıfının GetValue() metodunu kullandık.

Convert sınıfının diziler için olanını oluşturduktan sonra şimdi de DOS ekranına bir kutu çizen sınıf hazırlayalım. Kutuyu ╔, ═, ╗, ║, ╚, ve ╝ karakterleriyle oluşturacağız. Bu karakterlerin Unicode karşılıklarıysa:

Karakter Unicode karşılığı (16'lık sistemde)
2554
2550
2557
2551
255A
255D

Microsoft Word'u açıp "Ekle" menüsünden "Simge"yi seçtğinizde bunlar gibi daha birçok simgeye erişebilirsiniz. Simgeleri direkt kopyala-yapıştır yapabileceğiniz gibi Unicode karşılıklarını öğrenip programınızda bu kodları da kullanabilirsiniz. Ancak eğer kopyala-yapoştır yapmışsanız kod dosyanızı (program.cs'nizi) kaydederken kodlamasını Unicode olarak değiştirin. Kodu Not Defteri'nde yazıyorsanız kaydetmek istediğinizde zaten sistem sizi uyaracaktır. Şimdi sınıfı yazmaya başlayalım:

 using System;
 class Buton
 {
    public int Genislik;
    public int Yukseklik;
    public void Ciz()
    {
       string[,] dizi=new string[Yukseklik+2,Genislik+3];
       dizi[0,0]="╔";
       dizi[0,Genislik+1]="╗";
       dizi[Yukseklik+1,0]="╚";
       dizi[Yukseklik+1,Genislik+1]="╝";
       for(int i=1;i<Genislik+1;i++)
       {   
          dizi[0,i]="═";
          dizi[Yukseklik+1,i]="═";
       }
       for(int i=0;i<=Yukseklik;i++)
          dizi[i,Genislik+2]="\n";
       for(int i=1;i<=Yukseklik;i++)
       {
          dizi[i,0]="║";
          dizi[i,Genislik+1]="║";
       }
       for(int j=1;j<=Yukseklik;j++)
          for(int i=1;i<=Genislik;i++)
             dizi[j,i]=" ";
      foreach(string i in dizi)
         Console.Write(i);
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Buton buton1=new Buton();
       buton1.Genislik=20;
       buton1.Yukseklik=13;
       buton1.Ciz();
    }
 }

Buton sınıfımız verilen ölçülerde butona benzeyen bir kutu oluşturmaktadır. Programdaki Genislik ve Yukseklik özelliklerimiz butona sığacak yatayda ve dikeydeki karakter sayısını belirtiyor. Tabii ki bu programı daha da geliştirebilirsiniz. Örneğin Buton sınıfına bir de metin özelliği ekleyebilirsiniz ve bu özellik butonun üstündeki yazıyı belirtebilir. Veya verilen ölçülerde bir tablo yapan veya kendisine parametre olarak verilen iki boyutlu bir diziyi ekrana bir tablo gibi çizen bir sınıf tasarlayabilirsiniz. Yani her şey hayal gücünüze kalmış.

NOT: Aslında bir stringi char türünden bir diziymiş gibi düşünebiliriz. Örneğin aşağıdaki ifadeler mümkündür:

 string a="deneme";
 char b=a[2];
 Console.Write(b);

Bu program ekrana n yazar. Ancak stringin herhangi bir karakteri bu yöntemle değiştirilemez. Bu yöntemle string karakterlerine sadece okuma (read-only) amaçlı erişebiliriz. Örneğin aşağıdaki örnek hatalıdır:

 string a="deneme";
 a[2]='ş';
 Console.Write(a);

string türündeki bir sabit ya da değişken dizilerin bazı özelliklerini sağlarken bazı özelliklerini sağlamaz:

  • Tıpkı dizilerdeki gibi stringlerde de foreach deyimi kullanılabilir.
  • Dizilerdeki Length özelliği stringlerle de kullanılabilir. Bunun dışındaki Array sınıfına ait hiçbir metot ve özellik stringlerle kullanılamaz.

NOT: Herhangi bir sahte özelliğin set veya get bloklarından yalnızca birini yazarak o özelliği salt okunur veya salt yazılır hâle getirebiliriz. Örneğin Array sınıfının Length özelliği salt okunur bir özelliktir.

NOT: C# 2.0'da ya da daha üst versiyonlarda bir sahte özelliğin set ve get bloklarını ayrı ayrı private veya public anahtar sözcükleriyle belirtebiliyoruz. Yani bir özelliğin bulunduğu sınıfın dışında salt okunur ya da salt yazılır olmasını sağlayabiliyoruz. Ancak az önce de söylediğim gibi bu özellik C# 1.0 veya C# 1.1 gibi daha önceki versiyonlarda geçerli değildir.

NOT: get ve set anahtar sözcükleriyle erişim belirleyiciler kullanırken uymamız gereken bazı özellikler vardır:

  1. Daima özellik bildiriminde kullanılan erişim belirleyicisi get veya set satırında kullanılan erişim belirleyicisinden daha yüksek seviyeli olmalıdır. Örneğin özellik bildiriminde kullanılan erişim belirleyici private ise get veya set satırında kullanılan erişim belirleyici public olamaz.
  2. get veya set satırında kullanılan erişim belirleyici özellik bildiriminde kullanılan erişim belirleyiciyle aynı olmamalıdır. (Zaten gereksizdir.)
  3. get ve set'ten ikisi için aynı anda erişim belirleyicisi kullanamayız.
  4. Yani işin özü set ve get satırlarındaki erişim belirleyicileri yalnızca, özelliği public olarak belirtmiş ancak özelliğin set veya get bloklarının herhangi birisini private yapmak istediğimizde kullanılabilir.
  5. get veya set için erişim belirleyicisi kullanacaksak sahte özelliğin bloku içinde hem get hem de set blokunun olması gerekir.

NOT: Aşağıdaki gibi bir kullanım da mümkündür:

class Sinif
{
    public int Sayi
    {
        get;
        set;
    }
}

class Program
{
    static void Main()
    {
        Sinif s = new Sinif();
        s.Sayi = 8;
    }
}

Bu program arkaplanda bir private "Sayi" özelliği varmış gibi davranır. Bu programın aşağıdaki programdan işlevsel olarak farkı yoktur.

class Sinif
{
    public int Sayi;
}

class Program
{
    static void Main()
    {
        Sinif s = new Sinif();
        s.Sayi = 8;
    }
}

Peki neden basit bir özellik yerine daha uzun get ve set'li sahte özellik tercih edelim? Çünkü get ve set'li sahte özelliklerde get veya set'ten birini private yapabiliriz. Bu sayede bu sahte özelliğin tanımlandığı sınıfın dışında salt-okunur veya salt-yazılır olmasını sağlayabiliriz. get ve set'in ikisini de private yapamayız. get ve set'in ikisini de private yapmak istersek özelliğin kendisini private yaparız. Bloksuz get ve set'in bloklu get ve set'ten önemli bir farkı vardır. Bloksuz get veya set bildiriminden herhangi birini yaptığımızda diğer get veya set bildirimini de yine bloksuz olarak yapmalıyız. Hatırlarsanız bloklu set/get bildiriminde get ve set'ten sadece birini yazma imkanımız vardı.

Basit özelliklere benzer şekilde bloksuz get/set'li sahte özelliklerin de varsayılan değerleri olabilir. Örnek:

using System;
class Sinif
{
    public int Ozellik { get; set; } = 30;
}

class Program
{
    static void Main()
    {
        Sinif s=new Sinif();
        Console.WriteLine(s.Ozellik);
    }
}

Gördüğünüz üzere sahte özelliklere değer atanmaya çalışıldığında sahte özelliğin set bloku, sahte özelliklerin değerleri alınmak istendiğinde sahte özelliğin get bloku çalışıyor. Aslında bir üçüncü durum daha var. Bu durumda sahte özelliğin hem get hem de set bloku çalışıyor. Örnek:

class Program
{
    static void Main()
    {
        Console.Write("Ozellik++ ");
        Ozellik++;
        Console.WriteLine("----------");
        Console.Write("Ozellik-- ");
        Ozellik--;
        Console.WriteLine("----------");
        Console.Write("++Ozellik ");
        ++Ozellik;
        Console.WriteLine("----------");
        Console.Write("--Ozellik ");
        --Ozellik;
    }
    static int Ozellik
    {
        set
        {
            Console.Write("set edildi: ");
            Console.WriteLine(value);
        }
        get
        {
            Console.WriteLine("get edildi");
            return 0;
        }
    }
}

Bu programın ekran çıktısı şöyle olur:

Ozellik++ get edildi
set edildi: 1
----------
Ozellik-- get edildi
set edildi: -1
----------
++Ozellik get edildi
set edildi: 1
----------
--Ozellik get edildi
set edildi: -1

Gördüğünüz gibi özellik, önden veya arkadan ++ veya -- operatörleriyle kullanıldığında önce özelliğin get bloku sonra set bloku çalışıyor. Özelliğin set bloku çalıştığında özelliğe 1 veya -1 atanmış varsayılıyor.

Yapıcı metotlar[değiştir]

Şimdiye kadar bir sınıfın üye elemanlarını (metotlar ve özellikleri) kullanabilmek için o sınıf türünden bir nesne oluşturuyorduk ve bu nesne üzerinden o sınıfın üye elemanlarına erişebiliyorduk. Bir sınıf türünden bir nesne oluşturduğumuzda -new anahtar sözcüğü sayesinde- o sınıftaki bütün özellikler ve metotlar başka bir bellek bölümüne kopyalanıyor ve bütün özellikler -sınıfta bir değer atanmamışsa- varsayılan değeri tutuyordu. Peki bir sınıf türünden bir nesne oluşturduğumuzda sınıftaki bütün özelliklerin varsayılan değeri tutması yanında başka şeyler de yapılmasını ister miydiniz? İşte bunun için yapıcı metotları kullanıyoruz. Yapıcı metotlarla ilgili bilmemiz gereken şeylerse:

  • Yapıcı metotların adı sınıfın adıyla aynı olmalıdır.
  • Yapıcı metotlar bir değer tutamaz. Ancak normal metotlardan farklı olarak void anahtar sözcüğü de kullanılmaz. Örnek program:
 using System;
 class Deneme
 {
    public Deneme()
    {
       Console.WriteLine("Deneme sınıf türünden bir nesne oluşturuldu.");
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Deneme a=new Deneme();
    }
 }

Bu program ekrana Deneme sınıf türünden bir nesne oluşturuldu. yazacaktır. Bunu bu şekilde kullanabilirsiniz. Ancak bunun asıl kullanımı sınıftaki özellikleri varsayılan değerden farklı bir değere çekmektir. Yapıcı metotların şu şekilde kullanımı da mümkündür.

 using System;
 class Deneme
 {
    public Deneme(int a,int b,int c)
    {
       Console.WriteLine(a+b+c);
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Deneme a=new Deneme(2,5,6);
    }
 }

Gördüğünüz gibi yapıcı metot parametre de alabiliyor. Yapıcı metoda parametreler ise nesne oluşturulurken veriliyor.

NOT: İllaki yapıcı metodun asıl sınıf dışında bulunmasına gerek yoktur. Örneğin aşağıdaki gibi bir kullanım da mümkündür:

 using System;
 class Deneme
 {
    Deneme()
    {
       Console.WriteLine("Bu sınıf türünden bir nesne oluşturuldu.");
    }
    static void Main()
    {
       Deneme a=new Deneme();
    }
 }

Bu durumda yapıcı metodu public olarak belirtmeye gerek yok.

NOT: Yapıcı metotların erişim belirleyicilerini private yaparak sınıfın dışından o sınıf türünden nesne oluşturulmasını engellemiş oluruz.

Varsayılan yapıcı metot[değiştir]

Biz herhangi bir yapıcı metot oluşturmamışsak C# otomatik olarak bir yapıcı metot oluşturur. Bu metodun içi boştur, yani bir şey yapmaz. Bu metoda varsayılan yapıcı metot denir. Bu varsayılan yapıcı metodun aldığı herhangi bir parametre yoktur. Varsayılan yapıcı metotla ilgili bilmemiz gerekenler:

  • Eğer bir sınıfta herhangi bir yapıcı metot oluşturmuşsak varsayılan yapıcı metot oluşturulmaz.
  • Yapıcı metotlar da aşırı yüklenebilir. Örneğin public Deneme();, public Deneme(int a);, public Deneme(int a, int b); ve public Deneme(int a,int b,int c); şeklinde üç satır oluşturabilir ve nesnenin oluşturulma şekline göre bu metotlardan yalnızca birinin çalıştırılmasını sağlayabiliriz. Örnek:
 using System;
 class Deneme
 {
    Deneme()
    {
       Console.WriteLine(0);
    }
    Deneme(int a)
    {
       Console.WriteLine(a);
    }
    Deneme(int a,int b)
    {
       Console.WriteLine(a+b);
    }
    Deneme(int a,int b,int c)
    {
       Console.WriteLine(a+b+c);
    }
    static void Main()
    {
       Deneme a=new Deneme(5,6);
    }
 }

Bu örnekte üçüncü metot çalıştırılacaktır. Bu örneği şöyle değiştirebiliriz.

 using System;
 class Deneme
 {
    Deneme(int a,int b,int c)
    {
       Console.WriteLine(a+b+c);
    }
    Deneme():this(0,0,0)
    {
    }
    Deneme(int a):this(a,0,0)
    {
    }
    Deneme(int a,int b):this(a,b,0)
    {
    }
    static void Main()
    {
       Deneme a=new Deneme(5,6);
    }
 }

Bu örnekte this anahtar sözcüğü sayesinde ikinci, üçüncü ve dördüncü yapıcı metotlar içeriğini aynı isimli ve üç parametre alan metottan alıyor. this anahtar sözcüğüyle kullanılan ikinci, üçüncü ve dördüncü yapıcı metotların yaptığı tek iş, birinci yapıcı metoda belirli parametreleri göndermek oluyor.

NOT: this anahtar sözcüğü bu şekilde yalnızca yapıcı metotlarla kullanılabilir.
NOT: Eğer bir sınıfta parametre alan bir veya daha fazla yapıcı metot varsa ve parametre almayan yapıcı metot yoksa -bu durumda varsayılan yapıcı metot oluşturulmayacağı için- Sinif nesne=new Sinif(); gibi bir satırla parametre vermeden nesne oluşturmaya çalışmak hatalıdır. Çünkü Sinif nesne=new Sinif(); satırı Sinif sınıfında parametre almayan bir yapıcı metot arar, bulamaz ve hata verir. Örnek:

 
 class A
 {
    public A(int a){}
 }
 class B
 {
    A a=new A();
 }

Bu program derleme zamanında hata verir.

Yıkıcı metotlar[değiştir]

Değişkenlerdeki faaliyet alanı kuralları aynen nesnelerde de geçerlidir. Yani Deneme a=new Deneme(); satırıyla tanımladığımız a nesnesi üzerinden Deneme sınıfının üye elemanlarına, yalnızca Deneme a=new Deneme(); satırının içinde bulunduğu en iç bloktan erişilebilir. C++ gibi alt seviye programlama dillerinde bir nesnenin faaliyet alanı bittiğinde manuel olarak bunun bilgisayara söylenmesi gerekebiliyordu. Bu söyleme işi ise yıkıcı metotlarla oluyordu. Aksi bir durumda bellekten söz konusu nesneler silinmediği için karışıklıklar çıkabiliyordu. Halbuki C#'ta bizim böyle bir şey yapmamıza gerek yok. C# Garbage Collection (çöp toplama) mekanizması sayesinde gereksiz nesneleri bellekten siliyor. Ancak bunun illaki nesnenin faaliyet alanı sonlanır sonlanmaz yapılacağı garantisi yok. Örnek bir program:

 using System;
 class Deneme
 {
    ~Deneme()
    {
       Console.WriteLine("Yıkıcı metot şimdi çalıştırıldı.");
    }
    static void Main()
    {
       {
          Deneme a=new Deneme();
       }
       Console.WriteLine("Vikikitap");
    }
 }

Gördüğünüz gibi yıkıcı metotlar sınıf adının başına ~ işareti getirilerek oluşturuluyor. Yıkıcı metotlar herhangi bir değer tutamazlar (void de almazlar) ve herhangi bir parametre almazlar. Bu program a nesnesi bellekten silindiği anda yani yıkıcı metot çalıştırıldığı anda ekrana Yıkıcı metot şimdi çalıştırıldı. yazacaktır.

Static üye elemanlar[değiştir]

Bu konuya daha önceden değinmiştik. Ancak bu bölümde biraz hatırlatma ve biraz da ek bilgi verme gereksinimi görüyorum.

  • Bir nesneye bağlı olarak çalışmayacak üye elemanları static olarak belirtiriz. Örneğin Console sınıfındaki WriteLine() metodu veya Math sınıfındaki PI özelliği static üye elemanlardır.
  • Static üye elemanlara nesne oluşturarak ulaşmaya çalışmak hatalıdır.
  • Static üye elemanları oluştururken static sözcüğünün erişim belirleyici sözcükle sırası önemli değildir. Yani static public int Topla() veya public static int Topla() kullanımlarının ikisi de doğrudur. Ancak tabii ki geri dönüş tipinin üye eleman adının hemen öncesinde gelmesi gerekir.
  • Şimdiye kadar fark ettiğiniz gibi Main metodunu hep static olarak oluşturduk. Eğer static olarak oluşturmasaydık bu metodun çalışabilmesi için içinde bulunduğu sınıf türünden bir nesne oluşturmamız gerekecekti. Bu durumda da Main metodu işlevi ile çelişecekti. Çünkü Main metodu programımızın çalışmaya başladığı yerdir.
  • static olarak tanımlanan üye elemanlara çağrı yapıldığı an bu üye elemanlar için dinamik olarak bellekte yer ayrılır. Bu sayede static olarak tanımlanan ama değer atanmayan özelliklerin çağrı yapıldığı an varsayılan değeri tutması sağlanır.
  • Bir sınıf nesnesi oluşturulduğunda static üye elemanlar için bellekte ayrı bir yer ayrılmaz.
  • Normal metotlar gibi yapıcı metotlar da static olabilirler. örnek:
 using System;
 class Deneme
 {
    static Deneme()
    {
       Console.WriteLine("Static metot çağrıldı.");
    }
    Deneme()
    {
       Console.WriteLine("Static olmayan metot çağrıldı.");
    }
    static void Main()
    {
       Deneme a=new Deneme();
       Deneme b=new Deneme();
    }
 }

Bu program ekrana şunları yazacaktır:

Static metot çağrıldı.    
Static olmayan metot çağrıldı.
Static olmayan metot çağrıldı.

Gördüğünüz gibi bir sınıf türünden bir nesne oluşturuldığında önce static metot sonra (varsa) static olmayan metot çalıştırılıyor. Sonraki nesne oluşturumlarında ise static metot çalıştırılmıyor. Static yapıcı metotlar parametre veya erişim belirleyicisi almazlar. Şimdi biraz kapsamlı bir örnek yapalım:

 using System;
 class Oyuncu
 {
    static int Toplam;
    Oyuncu()
    {
       Toplam++;
       Console.WriteLine("Toplam oyuncu: "+Toplam);
    }
    static Oyuncu()
    {
       Console.WriteLine("Oyun başladı");
    }
    ~Oyuncu()
    {
       Console.WriteLine("Bir oyuncu ayrıldı...");
       Toplam--;
    }
    static void Main()
    {
       { 
          Oyuncu ahmet=new Oyuncu();
          Oyuncu osman=new Oyuncu();
       }
       Oyuncu ayse=new Oyuncu();
       Oyuncu mehmet=new Oyuncu();
    }
 }

Bu programın ekran çıktısı şöyle olacaktır.

Oyun başladı
Toplam oyuncu: 1
Toplam oyuncu: 2
Toplam oyuncu: 3
Toplam oyuncu: 4
Bir oyuncu ayrıldı...
Bir oyuncu ayrıldı...
Bir oyuncu ayrıldı...
Bir oyuncu ayrıldı...

Daha önce yıkıcı metodun, nesnenin kapsama alanı sonlanır sonlanmaz çalıştırılma garantisinin olmadığını söylemiştim. Örneğin bu örnekte program kendini sonlandırırken her bir nesne için yıkıcı metotlar çalıştırılıyor (nesnenin kapsamı alanı sonlanır sonlanmaz değil).

Bir nesneyi hangi yapıcı metotla oluşturursak oluşturalım (parametreli olabilir) statik yapıcı metot mutlaka ilk nesne oluşturulur oluşturulmaz normal yapıcı metottan önce çalıştırılır.

Static sınıflar[değiştir]

Eğer bir sınıf sadece static elemanlar içeriyorsa o sınıfı static olarak tanımlayabiliriz. Böylelikle derleyici bize o sınıf türünden bir nesne oluşturmamıza izin vermeyecektir. Açıkçası pratikte pek bir faydası yoktur. Çünkü static olarak tanımladığımız sınıfların üye elemanlarını da ayrıca static olarak tanımlamak zorundayız. Static sınıfların yapıcı metotları olmaz. Dolayısıyla yapıcı metot tanımlamaya çalışırsak derleyici hata verir. Klasik bir static sınıf bildirimi şöyle yapılır:

 static class SinifAdi
 {
 ...
 }

NOT: Statik sınıflar statik olmayan üye eleman içeremez.

NOT: Statik sınıflar statik olmayan yapıcı metot içeremez. Ancak statik yapıcı metot içerebilirler. Statik yapıcı metot çoğunlukla sınıftaki statik özelliklerin ilk değerlerini atamak için kullanılır.

NOT: Değişkenler konusunda gördüğümüz const anahtar sözcüğü özelliklerle de kullanılabilir. const olarak tanımlanan özellikler aynı zamanda static'tir. Dolayısıyla const özellikleri tekrar static anahtar sözcüğüyle belirtmek hatalıdır. const özellikler static özelliklerin taşıdığı tüm özellikleri taşır. const özelliklere const değişkenlerde olduğu gibi tanımlanır tanımlanmaz değer verilmeli ve değer verirken de sabit, sabit özellik veya sabit ve/veya sabit özelliklerden oluşan matematiksel ifadeler kullanılmalıdır.

NOT: const anahtar sözcüğü değer tipleriyle ve referans tipleriyle kullanılabilir. Ancak string dışındaki referans tiplerindeki değişken ve özelliklere sadece null değer atanabilir.

UYARI: Bir sınıfın içinde (bir metodun veya sahte özelliğin içinde olmadan) metot veya özellik oluşturulmasından başka bir şey yapılamaz. Yapılacak diğer bütün şeyler bir metot blokunun veya sahte özelliğin get veya set blokunun içinde olmalıdır.

using deyimi ile statik üyelere direkt erişim[değiştir]

C# 6.0 ile birlikte C# programcıları güzel bir özelliğe kavuşmuştur. C# 6.0'dan itibaren using static anahtar sözcükleriyle herhangi bir sınıfı program kodumuzun başına eklersek (tıpkı isim alanlarını eklediğimiz gibi) o sınıftaki bütün statik üye elemanları programımızda direkt kullanabiliriz. Örnek:

using System;
using static System.Console;
class Program
{
   static void Main()
   {
      WriteLine("Deneme");
   }
}

İstersek burada Console sınıfı dışında bir sınıfı kullanmayacaksak using System; satırını kaldırabiliriz.

NOT: using static deyimiyle mutlaka tam sınıf yolunu kullanmalıyız. Örneğin daha önce using System; satırı ile System isim alanındaki sınıflara erişim hakkı elde etmiş olsak bile sadece using static Console; yazarak bu isim alanındaki Console sınıfını programımıza ekleyemeyiz.

readonly anahtar sözcüğü[değiştir]

const anahtar sözcüğüyle sadece değer tipleri ve string tipindeki değişken ve özellikleri tanımlayabiliriz. string dışındaki referans tipindeki değişken ve özellikleri const olarak tanımlarsak bunlara sadece null değer atayabiliriz. Bu da pratikte fayda sağlamayacak bir durumdur. İşte C#'ta bu eksikliği gideren readonly anahtar sözcüğü vardır. readonly olarak tanımlanan özellikler içsel olarak statik değildir, ayrıca statik olarak tanımlanmaları gerekir. readonly ile hem değer tiplerindeki hem referans tiplerindeki özellikleri salt-okunur olarak belirtebiliriz. readonly anahtar sözcüğüyle tanımlanan özelliklerin diğer özellikleri şunlardır:

  • readonly olarak tanımlanan özelliklere tanımlandıkları ilk satırda değer verilmesi zorunlu değildir. Eğer bu özelliğe değer verilmeden erişilirse o özelliğinin tipinin varsayılan değeri elde edilir.
  • readonly olarak tanımlanan özelliklerin değerlerini program içinde iki yerde belirleyebiliriz. Ya özellik ilk tanımlanırken ya da yapıcı metot içinde. Hem özellik tanımlanırken hem de yapıcı metot içinde readonly özelliğe değer atanabilir. Bu durumda yapıcı metot içindeki atama gerçekleşir. Bu iki yer dışında readonly özelliklerin değerleri değişemez.
  • Sadece özellikler readonly olarak tanımlanabilir. Değişkenler readonly olarak tanımlanamaz.
  • readonly özelliklere değer olarak tipiyle uyumlu olmak şartıyla her şeyi atayabiliriz. readonly özelliğe atadığımız şeylerin readonly veya const olma zorunluluğu yoktur. Örneğin:
class Program
{
    readonly string s = System.Console.ReadLine();
    static void Main()
    {
       Program p=new Program();
       System.Console.WriteLine(p.s);
    }
}
  • readonly anahtar sözcüğü ile tanımlanan özelliklerin değerleri atama veya işlemli atama operatörüyle değiştirilemez. Ancak özelliğin döndürdüğü nesnenin herhangi bir özelliği değiştirilebilir. Örnek:
using System;
class Program
{
    readonly Sinif ProgramOzellik;
    Program(Sinif s)
    {
        ProgramOzellik=s;
    }
    static void Main()
    {
       Program p=new Program(new Sinif(5));
       Console.WriteLine(p.ProgramOzellik.SinifOzellik); //çıktı: 5
       p.ProgramOzellik.SinifOzellik=10;
       Console.WriteLine(p.ProgramOzellik.SinifOzellik); //çıktı: 10
    }
}
class Sinif
{
    public int SinifOzellik;
    public Sinif(int i)
    {
        SinifOzellik=i;
    }
}

Burada ProgramOzellik özelliği readonly olmasına rağmen bu özellik üzerinden eriştiğimiz SinifOzellik özelliğini değiştirebildik. Çünkü SinifOzellik özelliği readonly veya const değildi. NOT: Aslında int a; satırı int türünden bir nesne bildirimidir ve int a=new int(); satırı mümkündür. Bu durumda a'ya int türünün varsayılan değeri atanır. Bunlarla ilgili daha fazla bilgiyi ileride göreceğiz.

Yapıcı metot kullanmadan nesne oluşturma[değiştir]

İstenirse yapıcı metot kullanmadan da nesne oluşturulabilir. Örnek:

class Program
{
    static void Main()
    {
        Ogrenci ogrenci1 = new Ogrenci("Bekir", "OFLAZ");
        Ogrenci ogrenci2 = new Ogrenci
        {
            Ad = "Ayşe",
            Soyad = "OFLAZ",
        };
        Ogrenci ogrenci3 = new Ogrenci
        {
            OgrenciNo = 183
        };
        Ogrenci ogrenci4 = new Ogrenci
        {
            Ad = "Hatice",
            Soyad = "ÖZDOĞAN",
            OgrenciNo = 116
        };
        Ogrenci ogrenci5 = new Ogrenci{};
    }
}

class Ogrenci
{
    public Ogrenci() { }
    public Ogrenci(string ad, string soyad)
    {
        Ad = ad;
        Soyad = soyad;
    }
    public string Ad { get; set; }
    public string Soyad { get; set; }
    public int OgrenciNo { get; set; }
}

Burada hem yapıcı metot kullanarak hem de yapıcı metot kullanmadan nesne oluşturulması gösterilmiştir. Gördüğünüz gibi yapıcı metot kullanmadan nesne oluşturulmasında sınıfın bir veya daha fazla public özelliğine değer atanabiliyor, değer atanmayan public özellikler varsayılan değerde kalıyor. Ancak burada "yapıcı metot kullanmadan" dememize rağmen aslında özelliklere değer atanmadan önce gizlice varsayılan yapıcı metot çağrılmaktadır. Bu yüzden yapıcı metot kullanmadan nesne oluşturulabilmesi için sınıfın varsayılan yapıcı metodu olması gerekir. Bildiğiniz üzere sınıfta parametre alan yapıcı metot tanımlarsak varsayılan yapıcı metot kullanım dışı kalır. Bu yüzden bu örnekte hiçbir şey yapmasa da parametre almayan bir yapıcı metot oluşturulmuştur. Main metodunun son satırında (Ogrenci ogrenci5 = Ogrenci{};) ise Ogrenci tipinden nesne oluşturulmuş ama hiçbir özelliğe değer atanmamıştır. Bu durumda sadece varsayılan yapıcı metot çalışmıştır.

NOT: Buradaki "yapıcı metot kullanmadan" deyiminden kasıt bilinçli olarak yapıcı metodun çalıştırılmamasıdır. Yoksa yine de gizlice varsayılan yapıcı metot çalıştırılır.

NOT: C# dilinde statik yapıcı metotlar bir uygulamada ancak ve ancak bir nesne yaratıldığında veya o sınıfın statik bir üye elemanına erişildiğinde çalışır. İlk önce hangi işlem yapıldıysa (nesne yaratılması veya statik üye elemana erişilmesi) o zaman statik yapıcı metot çalışır. Onun dışında statik yapıcı metot çalışmaz. Örnek:

using System;
class Program
{
    static void Metot1()
    {
        Console.WriteLine("Metot1 metodu çalıştı.");
    }
    static Program()
    {
        Console.WriteLine("Statik yapıcı metot çalıştı.");
    }
    Program()
    {
        Console.WriteLine("Statik olmayan yapıcı metot çalıştı.");
    }
    static void Metot2()
    {
        Console.WriteLine("Metot2 metodu çalıştı.");
    }
    static void Main()
    {
        Metot1();
        Program p=new Program();
        Metot2();
    }
}

Bu programın ekran çıktısı şöyle olacaktır:

Statik yapıcı metot çalıştı.
Metot1 metodu çalıştı.
Statik olmayan yapıcı metot çalıştı.
Metot2 metodu çalıştı.

Programda Main blokunda önce Metot1() metoduna erişilmiştir. Program hayatı boyuncaki ilk statik üye eleman çağrısı veya nesne oluşturulma talebini aldığı için öncelikle statik yapıcı metodu çalıştırmıştır. Henüz herhangi bir nesne oluşturulmadığı için statik olmayan yapıcı metot çalıştırılmamıştır. Çünkü statik olmayan yapıcı metotlar sadece nesne oluşturulduğunda çalıştırılır. Statik yapıcı metodun çalıştırılmasından sonra doğal olarak Metot1() metodunun kendisi çalıştırılmıştır.

Main metodunun ikinci satırında Program sınıfı tipinden nesne oluşturulmaktadır. Daha önce statik yapıcı metot çalıştırıldığı için artık statik yapıcı metot çalıştırılmamıştır, ancak doğal olarak nesne yaratıldığı için statik olmayan yapıcı metot çalıştırılmıştır.

Üçüncü satırda ise sadece Metot2() metodunun kendisi çalıştırılmıştır. Statik yapıcı metot daha önce çalıştırıldığı için çalıştırılmamıştır. Üçüncü satır bir nesne yaratım talebi olmadığı için statik olmayan yapıcı metot da çalıştırılmamıştır.

NOT: Statik yapıcı metotlar içsel olarak zaten public'tir. O yüzden erişim belirleyicisi alamazlar.

Bu kitabın diğer sayfaları
  • Sınıflar
  • Operatör aşırı yükleme
  • İndeksleyiciler
  • Yapılar
  • Enum sabitleri
  • İsim alanları
  • System isim alanı
  • Temel I/O işlemleri
  • Temel string işlemleri
  • Kalıtım
  • Arayüzler
  • Partial (kısmi) tipler
  • İstisnai durum yakalama mekanizması
  • Temsilciler
  • Olaylar
  • Önişlemci komutları
  • Göstericiler
  • Assembly kavramı
  • Yansıma
  • Nitelikler
  • Örnekler
  • Şablon tipler
  • Koleksiyonlar
  • yield
  • Veri tabanı işlemleri
  • XML işlemleri
  • Form tabanlı uygulamalar
  • Visual Studio.NET
  • Çok kanallı uygulamalar
  • ASP.NET