30 Ekim 2013 Çarşamba

C# Bir Sınıfın Anatomisi


BİR SINIFIN ANATOMİSİ
Sınıf, belli özelliklere göre nesneleri gruplamaktır. Örneğin; çam, meşe vs gibi nesneler ağaç olarak sınıflandırılabilir. Aynı şekilde ağaç, çiçek, ot gibi sınıflar da gruplanarak bir üst sınıf olan bitkiler sınıfını oluşturur. Bitkiler, insanlar, hayvanlar da gruplanarak canlılar sınıfını oluşturmaktadır. Bu ağaç yapısını kökten başlayarak analiz edecek olursak; canlılar sınıfı temel sınıftır. Bitkiler, insanlar, hayvanlar bu temel sınıftan türemiş sınıflardır. Ağaç, çiçek, ot ise bitkiler sınıfından türemiş sınıflardır. En sonda ise çam, meşe vs. ağaç sınıfından türemiş nesnelerdir. Her sınıfın belli başlı özellikleri ve fonksiyonları vardır. Örneğin; ağaç sınıfının türü, boyu vs. gibi özellikleri ve fotosentez yapma gibi fonksiyonları vardır. Ağaç sınıfından türeyen her nesne bu temel özellikleri ve fonksiyonları miras almaktadır. Nesne yönelimli programlamadaki sınıf kavramının bu benzetmeden farkı yoktur. Bu makalede sınıf kavramı üzerinde duracağız. Sınıflar yazacak, türetme yapacak ve sınıflarda neler bulunur, bunları inceleyeceğiz.
Örnek olarak "Canlilar" sınıfını yazacak, sonra "Canlilar" sınıfından "Insan" sınıfını türetecek ve nihayetinde "Insan" sınıfından "Sercan" nesnesini oluşturacağız. Bu süreç içerisinde sınıflar içerisinde kullanılan alanlardan, özelliklerden, fonksiyonlardan, metodlardan, yapıcılardan, temsilcilerden yeri geldikçe bahsedeceğiz. Yapıcı metodlar ile başlayalım.
Bir sınıfın yeni bir kopyası oluşturulurken, sınıfın yapıcı metodu çalışmaktadır. Yapıcı metod sınıf ile aynı ada sahiptir. Varsayılan yapıcı metod parametre almaz. Ancak çeşitli parametreleri vererek birden fazla yapıcı metod tanımlayabiliriz. Buna aşırı yükleme denilmektedir. Yapıcı metod tanımlarken dikkat edilmesi gereken nokta metodun sınıf ile aynı ada sahip olması ve birden fazla yapıcı metod tanımlanmış ise metodların parametre sayılarının veya parametre türlerinin birbirinden farklı olmasıdır. Aşağıda "Canlilar" sınıfımız ve yapılandırıcıları verilmiştir.
class Canlilar
{
private static string tur;
private static int yas;
public Canlilar()
{
}
public Canlilar(string turu)
{
tur = turu;
}
public Canlilar(string turu, int yasi)
{
tur = turu;
yas = yasi;
}
}
İlk yapılandırıcımız varsayılan yapılandırıcıdır. Nesne herhangi bir parametre almadan oluşturulur. İkinci yapılandırıcımız canlının tür bilgisini alarak "Canlilar" sınıfının kopyasını oluşturmaktadır. Üçüncü metodumuz ise tür ve yaş bilgilerini alarak sınıfımızın kopyasını oluşturmaktadır. "Canlilar" sınıfını oluştururken girdiğimiz bu parametreler sınıf içerisinde tanımlanan "tur" ve "yas" değişkenlerine aktarılarak sınıf içerisindeki işlemlerde kullanılmaktadır. Değişkenleri ve yapıcıları tanımlarken private ve public belirteçlerini kullandık. Private belirteci tanımlanan değişkenin, metodun vs. sadece o sınıf içerisinden erişilebileceğini belirtmektedir. Aksi belirtilmediği sürece bütün değişkenler, metodlar private tanımlanmıştır. Public belirteci ile tanımlanan değişkenlere, metodlara vs. bütün sınıflardan erişim sağlanmaktadır. Bu iki belirtecin dışında hem sınıf içerisinden hem de referans gösterildiği diğer sınıflardan erişim sağlayan protected, içinde bulunduğu sınıfa ait assembyl tarafından erişim sağlayan internal belirteçleri vardır.
Her canlı sınıfının bir türü vardır. Tür, canlılar sınıfının sahip olduğu bir özelliktir. Şimdi Canlılar sınıfımıza tür özelliğini ekleyelim.
public string Tur
{
get { return tur; }
set { tur = value; }
}
"Tur" özelliği hem dışarıdan değer alan hemde içerdiği değeri çağrıldığı yere aktaran bir yapıdadır. Yani hem okunabilir hem yazılabilir. Set bloğunda aldığı değeri "tur" değişkenine aktarır. Get bloğunda ise "tur" değişkeninin değerini çağrıldığı yere return eder. Özellikler ReadOnly'de olabilir. Bunun için sadece get bloğu yazılır.
public string SinifBilgisi
{
get { return "Canlilar sınıfı."; }
}
"SinifBilgisi" adlı bu özellik kullanıldığında "Canlılar sınıfı." stringini return eder. "SinifBilgisi" özelliğine değer atadığımızda bu özelliğin ReadOnly olduğunu belirten bir hata ile karşılaşırız. Özelliklerin yanında sınıflarda bazı işlevleri yerine getiren fonksiyonlar olabilir. Örnek olarak "Canlilar" sınıfımızda dışarıdan canlının doğum tarihini alıp geriye yaşını return eden bir fonksiyon olsun.
public int YasHesapla(DateTime dogumTar)
{
int dogumTarihi = dogumTar.Year;
yas = Convert.ToInt32(DateTime.Now.Year) - dogumTarihi;
return yas;
}
Bir sınıf içerisinde yazdığımız bir fonksiyonun içeriğine, bu sınıftan türettiğimiz bir sınıfta eklemeler yapabilir veya içeriğini tamamen değiştirebiliriz. Bunun için sonradan içeriğine müdahele edebileceğimiz metodları virtual olarak tanımlamalıyız. Virtual metodlara, bu metodların bulunduğu sınıftan türemiş sınıflarda müdahele edilebilir. Örnek olarak canlılar sınıfımızda "TurYaz" adında canlının türünü ve yaşını return eden virtual bir metodu olsun.
public virtual string TurYaz()
{
return tur+" "+yas.ToString();
}
Şimdi, "Canlılar" sınıfından "Insan" sınıfını türetelim. Sınıf isminden sonra ":" koyup türeteceğimiz sınıfın ismini yazıyoruz. Unutmamak gerekir ki bir sınıf ancak bir tek sınıftan türeyebilir. Çoklu kalıtım için ilerleyen satırlarda değineceğimiz arayüzler(Interface) kullanılır.
class Insan:Canlilar
{
}
"Insan" sınıfını "Canlilar" sınıfından türettik. Artık "Insan" sınıfımız da yukarıda "Canlilar" sınıfı için yazdığımız "Tur" ve "SinifBilgisi" özellikleri ile "YasHesapla" ve "TurYaz" metodlarına sahiptir. Bu üyeler static tanımlanmadığından direkt erişilemezler. Öncelikle "Insan" sınıfından bir kopya oluşturmamız gerekmektedir. Hem buna hemde virtual metodların içeriğine müdahele edilmesine örnek olarak "Insan" sınıfımzıda "TurYaz" metodunu override edelim.
class Insan:Canlilar
{
private string ad;
public override string TurYaz()
{
Insan yeni = new Insan();
yeni.Tur = "Insan";
return "Bu canlı " + yeni.Tur+".Adı "+ad;
}
public string Ad
{
get { return ad; }
set { ad = value; }
}
}
İnsan sınıfının bir kopyasını oluşturduk. Bu şekilde "Tur" özelliğine erişebildik. Intellisense özelliği ile bunu gözlemleyebiliriz.

1.Şekil: Static tanımlama
Yukarıdaki koda geri dönersek, görüldüğü gibi "Insan" sınıfında "Tur" adında bir özellik tanımlamamamıza rağmen bu özelliği "Canlilar" sınıfından miras aldığı için kullanabiliyoruz. "Canlilar" sınıfından miras aldığı özellikler yanında sınıfımıza "Ad" adında yeni bir özellik ekledik. Yani sınıflarımıza türettiğimiz sınıfın özellikleri yanında yeni özellikler, fonksiyonlar vs. ekleyebiliriz. "TurYaz" metodunu "Canlilar" sınıfında tanımlamıştık ve kalıtım yolu ile "İnsan" sınıfına aktarmıştık. "Insan" sınıfından yeni bir üye tanımladığımızda ve "TurYaz" metodunu kullanmak istediğimizde ne olacak? Bu metodu kullandığımızda "Canlilar" sınıfındaki metodumuzda yazdığımız işlemler gerçekleşmeyecektir. Sebebi bu metodu "Insan" sınıfında override etmemizdir. Dolaysı ile "TurYaz" metodunu kullandığımızda yukarıda da görüldüğü gibi "Bu canlı " + canlının türü+".Adı "+canlının adı" katarı return edecektir. "Insan" sınıfından türeyen üçüncü bir sınıf daha yazalım.
class Sercan:Insan
{
public static void AdYaz()
{
Insan yeni = new Insan();
yeni.Ad = "Sercan YILMAZ";
MessageBox.Show(yeni.TurYaz());
}
}
"Sercan" sınıfı "Insan" sınıfından türemektedir. "AdYaz" adında bir metoda sahiptir. Bu metod içerisinde insanın adını bildiriyoruz. Bir mesaj penceresinde "TurYaz" metodunu çalıştırıyoruz. Bu satırda yukarıda override ettiğimiz metod çalışacaktır. Bu metod içerisinde canlı türünü "insan" olarak belirlemiştik. Geriye "Bu canlı insan.Adı Sercan Yılmaz" stringi return edecektir. Dolaysı ile mesaj penceresinde bu yazı görüntülenecektir. Bunun gibi .NET'in virtual metodlarını da override edebiliriz. Örnek olarak sıklıkla kullandığımız ToString() metodu sanal bir metoddur. Dolaysı ile override edebiliriz. Örnek olarak "Sercan" sınıfında bu metodu;
public override string ToString()
{
return "ToString metodu override yapıldı.";
}
şeklinde override edersem bu sınıfta ve bu sınıftan türetilen sınıflarda ToString() metodunu kullandığımızda "ToString metodu override yapıldı." katarı geriye dönecektir. "Canlilar" sınıfında "TurYaz" sanal metodunu yazdık. "Insan" sınıfında override ettik. "Sercan" sınıfında kullandık. Peki "Insan" sınıfında "Canlilar" sınıfındaki "TurYaz" metodunu nasıl kullanabilirim? Yani override edilmiş metodu değil de sanal metodu nasıl kullanabilirim. Bunun için "base" ifadesini kullanıyoruz. "Insan" sınıfında aşağıdaki metodu yazalım.
public string Turu()
{
return base.TurYaz();
}
Yukarıdaki "Turu" metodunun yaptığı iş taban sınıfın "TurYaz" metodundan dönen değeri return etmektir.
Son olarak event ekleyelim. Bildiğiniz gibi eventlar belli bir iş sonrasında tetiklenen metodlardır. Mesela buton tıklandığında Click olayı tetiklenir yada form yüklendiğinde Load olayı tetiklenir. Bunların dışında kendimiz de event yazabiliriz. Eventlar delegate türünden tanımlanır. Delegateler program içerisinde bir yada daha fazla metodu gösteren referans türünden nesnelerdir. Delegate tanımı aşağıdaki gibidir.
public delegate void Temsilci();
"void" temsilcinin(delegate) temsil ettiği metodun dönüş tipidir. Eğer temsil edilecek metod dışarıdan parametre alıyorsa bu da temsilcide belirtilmelidir. Mesela string bir değer return eden ve int türünden bir parametre alan metodu temsil eden delegate aşağıdaki gibi tanımlanır.
public delegate string Temsilci(int sayi);
"Sercan" sınıfında aşağıdaki gibi küçük bir event oluşturalım.
public string adim;
private delegate void Temsilci();
private event Temsilci Olay;
public void OlayOlustur()
{
Olay += new Temsilci(Sercan_Olay);
if (adim = = "Sercan")
{
Olay();
}
}
public void Sercan_Olay()
{
MessageBox.Show("Event çalıştı. ");
}
Önce eventı tanımlayacağımız temsilciyi tanımlıyoruz. Sonra eventı tanımlıyoruz. "OlayOlustur" adlı metodda önce tanımladığımız eventı oluşturuyor sonra eventı tetikliyoruz. Sınıf içerisindeki "adim" değişkeni "Sercan" değerini aldığında olay tetiklenmektedir. Sercan_Olay() metodu eventımızın çalıştıracağız metoddur.. Başka bir sınıfta;
Sercan  yeni = new Sercan();
yeni.OlayOlustur();
şeklinde "Sercan" sınıfının "OlayOlustur" adlı metodunu çalıştırırsak Sercan_Olay() eventı tetiklenir ve "Event tetiklendi." mesajı alınır. Bunun gibi herhangi bir işlem gerçekleştiğinde farklı işlerin tetiklenmesini istediğimizde kendi eventlarımızı yazabiliriz.
Sınıflarımız büyüdükçe takibide zorlaşmaktadır. Sınıfımızda neler kullandığımızı görmek için sınıfımızda kullandığımız metodları, özellikleri, olayları ve indeksleyicileri bir arayüz içerisinde tanımlarız. "Canlilar" sınıfımız için aşağıdaki gibi bir Interface yazabiliriz.
interface ICanlilar
{
int YasHesapla(DateTime dogum);
string Tur
{
get;
set;
}
string SinifBilgisi
{
get;
}
string TurYaz();
}
Görüldüğü gibi tanımlamalarda public, private gibi belirteç kullanılmamaktadır. Arayüzlerdeki tüm üyeler varsayılan olarak public'tir. "Canlilar" sınıfımızı bu arayüzden türetiyoruz. Arayüzlerden türetilen sınıflar arayüzlerde tanımlanan üyelerin hepsini kullanmak zorundadır. Yukarıda bir sınıfı ancak tek sınıftan türetebildiğimizi ifade etmiştik. Çoklu kalıtımı arayüzlerle gerçekleştiriyoruz. Birden fazla arayüz yazıp aşağıdaki gibi bir sınıfı bu arayüzlerden türetebiliriz.
class Canlilar:ICanlilar,ICanlilarYeni
{
}
Bu makaleden öğrendiklerimizi özetleyecek olursak;
  • Sınıf ve sınıf öğeleri (Constructer, Property, Event, Function, Event) tanımlama.
  • Erişim belirteçleri(Public, Private, Protected, Internal)
  • Virtual metod kullanma ve override işlemi
  • Kalıtım
  • Arayüz(Interface) tanımlama ve çoklu kalıtım
İş hayatının bu kadar yoğun olacağını düşünmemiştim:) Sonraki makalede görüşmek üzere.Bizden ayrılmayın:) 


Hiç yorum yok:

Yorum Gönder