C Sharp Programlama Dili/Kalıtım

Vikikitap, özgür kütüphane
Ders 20. Kalıtım


Diyelim ki elinizde A diye bir sınıf var. Ancak B diye bir sınıf daha oluşturmanız gerekiyor. Ancak bu B sınıfının içinde A sınıfındaki özellik ve metotların da bulunması gerekiyor. İşte bu durumda B sınıfını A sınıfından türetmeniz gerekir. Türetme kalıtım yoluyla olduğu için A sınıfının bütün üye elemanları B sınıfına adeta kopyalanır. Daha sonra B sınıfının kendine özel üye elemanlarını yazabiliriz. .Net kütüphanesindeki birçok sınıf birbirlerinden türetilmiştir. Örneğin temel veri türleri dediğimiz byte, int, uint, short, float ve bezerlerinin tamamı object sınıfından türetilmiştir. Bu sayede normalde object sınıfında bulunan ToString() metodunu bu yapı türünden nesnelerde de kullanabilirsiniz. Sınıflar türetilebilmesine rağmen yapılar türetilemez. Ancak bir sınıfın türetilmişinden yapı oluşturulabilir. C#'ta türetme şöyle yapılır:

 class A
 {
    ...
 }
 class B:A
 {
    ...
 }

Burada B sınıfı A sınıfından türetilmiştir ve artık B sınıfı A sınıfındaki bütün üye elemanları da içerir.

protected erişim belirleyicisi[değiştir]

Şimdiye kadar public ve private erişim belirleyicilerini görmüştük. Şimdi ise yeni bir erişim belirleyicisi olan protected'ı göreceğiz. Normalde bir sınıfı türettiğimizde türetilmiş sınıfın içinden ana sınıfta private olarak belirtilmiş üye elemanlara erişemeyiz. Ancak bu, private üye elemanların türetilmediği anlamına gelmez. Örneğin:

 using System;
 class A
 {
    static private int Ozellik;
 }
 class B:A
 {
    static void Main()
    {
       Console.WriteLine(Ozellik);
    }
 }

Burada B sınıfından Ozellik özelliğine erişilemez. Ancak halen Ozellik özelliği B sınıfında vardır (kopyalanmıştır). Eğer bu programda private yerine protected'ı kullansaydık Ozellik özelliğine erişebilirdik. Yani protected erişim belirleyicisi ile belirtilen üye elemanlar türetilmiş sınıfın içinden kullanılabilirler. Ancak halen diğer sınıfların erişimine kapalıdırlar. Eğer ortada bir türetme söz konusu değilse protected erişim belirleyicisinin private'ten farkı kalmaz.
NOT: Eğer türetilen bir metottan bir private üye elemana erişiliyorsa bu durumda bir engelleme gelmez. Bu da az önce söylediğimiz "Private erişim belirleyici türetilmeye engel değildir." tezini doğrular. Örnek:

 using System;
 class A
 {
    static private int Ozellik;
    static public void metot()
    {
       Console.WriteLine(Ozellik);
    }
 }
 class B:A
 {
    static void Main()
    {
       metot();
    }
 }

Bu program herhangi bir hata vermez.

Yapıcı metotlar ve kalıtım[değiştir]

C#'ta yapıcı metotların türetimiyle ilgili şu kurallar geçerlidir:

  1. C#'ta yapıcı metotlar fiziksel olarak türetilmez.
  2. Yavru sınıf türünden bir nesne yaratıldığında önce ana sınıfın parametre almayan yapıcı metodu, ardından yavru sınıftaki imzaya uyan yapıcı metot çalıştırılır.
  3. Yavru sınıf türünden nesne yaratımında daima yavru sınıfın imzaya uyan bir yapıcı metodu olması gerekir.
  4. Yavru sınıf türünden nesne yaratımlarında, ana sınıfın parametre almayan yapıcı metodu yavru sınıfın üye elemanlarıyla işlem yapar.
  5. Yavru sınıf türünden nesne yaratımında, yavru sınıftaki ilgili (imzası uygun) yapıcı metoda base takısı eklenmişse ana sınıfın parametre almayan yapıcı metodu çalıştırılmaz. (base takısını birazdan göreceğiz)

Şimdi bu kuralları örneklendirelim:

using System;
class ana
{
   public ana()
   {
      Console.WriteLine("ana sınıfının parametre almayan yapıcı metodu");
   }
}
class yavru:ana
{
   public yavru(int a)
   {
      Console.WriteLine("yavru sınıfının parametre alan yapıcı metodu. alınan parametre: "+a);
   }
}
class esas
{
   static void Main()
   {
      yavru y=new yavru(5);
   }
}

Bu programda ekrana alt alta ana sınıfının parametre almayan yapıcı metodu ve yavru sınıfının parametre alan yapıcı metodu. alınan parametre: 5 yazacaktır. Bu ikinci kuralın örneklendirilmesiydi. Şimdi üçüncü kuralı örneklendirelim:

using System;
class ana
{
   public ana()
   {
      Console.WriteLine("ana sınıfının parametre almayan yapıcı metodu");
   }
}
class yavru:ana{}
class esas
{
   static void Main()
   {
      yavru y=new yavru();
   }
}

Bu program hata vermez. Çünkü sınıflar konusunda öğrendiğimiz üzere bir sınıfta hiç yapıcı metot olmadığı durumlarda varsayılan yapıcı metot oluşturulur. Varsayılan yapıcı metot parametre almaz ve hiçbir şey yapmaz. Bu örnekte y nesnesini yaratırken parametre verseydik yukarıda bahsettiğimiz üçüncü kural ihlal edilmiş olacaktı ve dolayısıyla programımız hata verecekti. Şimdi dördüncü kuralı örneklendirelim:

using System;
class ana
{
   public int ozellik;
   public ana()
   {
      ozellik=5;
   }
}
class yavru:ana{}
class esas
{
   static void Main()
   {
      yavru y=new yavru();
      Console.WriteLine(y.ozellik);
   }
}

Bu örnekte ekrana 5 yazılacaktır. Aslına bakarsanız dördüncü kuralın tersinin olması imkansız. Çünkü zaten ortada ana tipinden bir nesne yok, o yüzden ana sınıfının parametre almayan yapıcı metodunun kendi sınıfının üye elemanlarıyla çalışması bu örnekte imkansız. Ancak yine de fiziksel olarak bir sınıfta olmayan bir yapıcı metodun o sınıfın üye elemanlarıyla çalışması ilginç. Şimdi başka bir örnek yapalım:

using System;
class ana
{
   public ana(int a){}
}
class yavru:ana{}
class esas
{
   static void Main()
   {
      yavru y=new yavru();
   }
}

Bu program hata verir. Çünkü yukarıda saydığımız ikinci kuralı ihlal etmektedir. Çünkü ana sınıfta parametre almayan bir yapıcı metot yoktur. Ana sınıfta bir yapıcı metot tanımlandığı için varsayılan yapıcı metot oluşturulmamıştır. Şimdi beşinci kuralı örneklendirelim:

using System;
class A
{
   public int Ozellik1;
   public int Ozellik2;
   public A()
   {
      Console.WriteLine("Deneme");
   }
   public A(int ozellik1,int ozellik2)
   {
      Ozellik1=ozellik1;
      Ozellik2=ozellik2;
   }
}
class B:A
{
   public int Ozellik3;
   public int Ozellik4;
   public B(int ozellik3,int ozellik4,int ozellik1,int ozellik2):base(ozellik1,ozellik2)
   {
      Ozellik3=ozellik3;
      Ozellik4=ozellik4;
   }
}
class esas
{
   static void Main()
   {
      B b=new B(3,4,1,2);
      Console.WriteLine(b.Ozellik1+" "+b.Ozellik2+" "+b.Ozellik3+" "+b.Ozellik4);
   }
}

Bu program ekrana 1 2 3 4 yazar. Bu örnekte base anahtar sözcüğü ana sınıftaki yapıcı metodu temsil etmektedir. Örneğimizde yavru sınıfın yapıcı metodu 4 parametre almakta, bu aldığı parametrelerin ikisini kendi blokunda kullanmakta kalan iki parametreyi de ana sınıfın imzası uygun yapıcı metoduna göndermektedir. Ana sınıfın imzası uygun yapıcı metodu çalıştığında yavru sınıfın üye elemanlarıyla işlem yapacaktır. Asıl konumuza gelecek olursak bu örnekte yavru sınıfın bir yapıcı metoduna eklenen base takısı ile ana sınıfın bir yapıcı metodunu çalıştırdığımız için yavru sınıftaki base takısı eklenmiş ilgili yapıcı metodu çalıştıracak şekilde yavru sınıf türünden bir nesne yaratıldığında ana sınıfın parametre almayan yapıcı metodu çalıştırılmayacaktır.
NOT1: base anahtar sözcüğü bu şekilde yalnızca yapıcı metotlarla kullanılabilir. Yani base anahtar sözcüğünü yalnızca yavru sınıftaki yapıcı metoda ekleyebiliriz ve base anahtar sözcüğünün ana sınıfta var olan bir yapıcı metodu belirtmesi gerekir.
NOT2: Yavru sınıfın yapıcı metoduna base takısı eklenip ana sınıfın bir yapıcı metodu çalıştırıldığı durumlarda önce ana sınıfın ilgili (imzaya uyan) yapıcı metodu, ardından yavru sınıfın ilgili (imzaya uyan) yapıcı metodu çalıştırılır. Örnek:

using System;
class A
{
   public A()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   public B():base()
   {
      Console.WriteLine("B sınıfı");
   }
}
class Ana
{
   static void Main()
   {
      B b=new B();
   }
}

Bu programda alt alta A sınıfı ve B sınıfı yazılacaktır. Şimdi isterseniz bir de statik yapıcı metotların kalıtımdaki rolüne bakalım:

using System;
class A
{
   static A()
   {
      Console.WriteLine("A sınıfının statik yapıcı metodu");
   }
   public A()
   {
      Console.WriteLine("A sınıfının statik olmayan yapıcı metodu");
   }
}
class B:A
{
   static B()
   {
      Console.WriteLine("B sınıfının statik yapıcı metodu");
   }
   public B()
   {
      Console.WriteLine("B sınıfının statik olmayan yapıcı metodu");
   }
}
class Ana
{
   static void Main()
   {
      B b1=new B();
      Console.WriteLine("-------");
      B b2=new B();
   }
}

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

B sınıfının statik yapıcı metodu
A sınıfının statik yapıcı metodu
A sınıfının statik olmayan yapıcı metodu
B sınıfının statik olmayan yapıcı metodu
-------
A sınıfının statik olmayan yapıcı metodu
B sınıfının statik olmayan yapıcı metodu

Gördüğünüz gibi statik yapıcı metotlarda yavru sınıf önce çalıştırılıyor. Statik olmayan yapıcı metotlarda ise ana sınıf önce çalıştırılıyor. İlk nesne yaratımından sonraki nesne yaratımlarında statik yapıcı metotlar çalıştırılmıyor.

Çoklu türetmeler[değiştir]

Sınıflar tıpkı nine, anne, çocuk yapısında olduğu gibi ard arda türetilebilir. Yani örneğin B sınıfı A sınıfında türetilip C sınıfı da B sınıfından türetilebilir. Bu durumda C sınıfı türünden bir nesne yarattığımızda eğer C sınıfının ilgili yapıcı metoduna base takısını eklememişsek önce A, sonra B, sonra da C sınıfının yapıcı metotları çalıştırılır. Yani gidişat anadan yavruya doğrudur. Ayrıca tahmin edebileceğiniz gibi C sınıfı hem A'nın hem de B'nin bütün üye elemanlarına sahip olur. Örnek:

 using System;
 class A
 {
    public A()
    {
       Console.WriteLine("A sınıfı");
    }
 }
 class B:A
 {
    public B()
    {
       Console.WriteLine("B sınıfı");
    }
 }
 class C:B
 {
    public C()
    {
       Console.WriteLine("C sınıfı");
    }
    static void Main()
    {
       C nesne=new C();
    }
 }

Bu program ekrana alt alta A sınıfı, B sınıfı ve C sınıfı yazacaktır. Bu örnekte base anahtar sözcüğünün kullanımı ise şöyledir:

 using System;
 class A
 {
    public int OzellikA;
    public A(int a)
    {
       OzellikA=a;
    }
 }
 class B:A
 {
    public int OzellikB;
    public B(int b,int a):base(a)
    {
       OzellikB=b;
    }
 }
 class C:B
 {
    public int OzellikC;
    public C(int c,int b,int a):base(b,a)
    {
       OzellikC=c;
    }
    static void Main()
    {
       C nesne=new C(12,56,23);
       Console.WriteLine(nesne.OzellikA+" "+nesne.OzellikB+" "+nesne.OzellikC);
    }
 }

Gördüğünüz gibi base anahtar sözcüğü kendisinin bir üstündeki sınıfın yapıcı metoduna parametre gönderiyor ancak tabii ki işlemler alt sınıftaki üye elemanlar için yapılıyor. Bu durumda -hangi sınıf türünden nesne yaratırsak yaratalım- hiçbir sınıfın parametre almayan yapıcı metodu çalıştırılmayacaktır (tabii ki nesne yaratırken gerekli parametreleri verdiğimiz müddetçe). Bu örneği şöyle değiştirirsek program hata verir:

using System;
class A
{
   public int OzellikA;
   public A(int a)
   {
      OzellikA=a;
   }
}
class B:A
{
   public int OzellikB;
   public B(int b)
   {
      OzellikB=b;
   }
}
class C:B
{
   public int OzellikC;
   public C(int c,int b):base(b)
   {
      OzellikC=c;
   }
   static void Main()
   {
      C nesne=new C(12,56);
      Console.WriteLine(nesne.OzellikA+" "+nesne.OzellikB+" "+nesne.OzellikC);
   }
}

Gördüğünüz gibi bu örnekte B sınıfının yapıcı metodundaki base takısı kaldırılmış ve gerekli düzenlemeler yapılmış. Bu örnekte B sınıfının yapıcı metodu çalıştırıldığı için ve B sınıfının söz konusu yapıcı metodunda base takısı olmadığı için B sınıfına göre ana sınıfın (bu durumda A sınıfı oluyor) parametre almayan yapıcı metodu çalıştırılmaya çalışılmıştır. A sınıfının parametre almayan yapıcı metodu olmadığı için program hata vermiştir. Yani ana sınıfın parametre almayan yapıcı metodu, tam olarak yavru sınıf tipinden nesne yaratılmasa da, sadece yavru sınıfın base takısı almayan yapıcı metodunun çalıştırılması durumunda da çalışır.

İsim saklama[değiştir]

Muhtemelen şunu merak etmişsinizdir: yavru sınıfta ana sınıftakiyle aynı isimli bir üye eleman varsa ne olacak? İşte bu durumda ana sınıftaki üye eleman gizlenir ve normal yöntemlerle erişilemez. Buna isim saklama denir. Örnek:

 using System;
 class A
 {
    public int a=10;
 }
 class B:A
 {
    public int a=20;
    static void Main()
    {
       B nesne=new B();
       Console.WriteLine(nesne.a);
    }
 }

Bu programda ekrana 20 yazılır yani değeri 20 olan a özelliği, değeri 10 olan a özelliğini gizlemiştir. Ancak derleyici bu programda bizim isim gizlemeyi bilinçsiz olarak yaptığımızı düşünür ve uyarı verir (hata vermez). Böyle bir durumda isim gizlemeyi açıkça belirtmeliyiz. Böylelikle hem programımızdaki muhtemel gözden kaçmaları hem de derleyicinin uyarı vermesini önleriz. İsim gizlemeyi açıkça belirtmek için new anahtar sözcüğünü kullanırız. Yukarıdaki programı şöyle yazarsak derleyici uyarı vermez:

 using System;
 class A
 {
    public int a=10;
 }
 class B:A
 {
    public new int a=20;
    static void Main()
    {
       B nesne=new B();
       Console.WriteLine(nesne.a);
    }
 }

Peki gizlediğimiz üye elemana erişebilir miyiz? Cevabımız evet. Bunun için base anahtar sözcüğünü kullanırız. Örnek:

 using System;
 class A
 {
    public int a=10;
 }
 class B:A
 {
    public new int a=20;
    int Metot()
    {
       return base.a;
    }
    static void Main()
    {
       B nesne=new B();
       Console.WriteLine(nesne.Metot());
    }
 }

Burada ekrana 10 yazılır. base anahtar sözcüğü static metotların içinde kullanılamaz.

NOT: C#, bir üye elemanın private olması nedeniyle ilgili üye eleman çağrılamadığında ana sınıfın aynı imzalı üye elemanını çağırmaya çalışır. Yani C#, private üye elemanları bulundukları sınıfın dışında kullanılmaya çalışıldığında yok sayar. Örnek:

using System;
class A
{
    public int a=10;
}
class B:A
{
    private new int a=20;
}
class Ana
{
    static void Main()
    {
       B nesne=new B();
       Console.WriteLine(nesne.a);
    }
}

Bu program ekrana 10 çıktısını verir.

Ana ve yavru sınıf nesneleri[değiştir]

C# tür güvenliğine maksimum derecede önem vermektedir. Bu yüzden, eğer özel tür dönüşüm operatörleri bildirilmemişse farklı sınıf türlerinden nesneler birbirlerine atanamaz. Ancak türeyen sınıflarda bu kural delinir. Ana sınıf türünden nesnelere yavru sınıf türünden nesneler atanabilir. Örneğin C#'taki temel veri türlerinin object sınıfından türetildiğini söylemiştik. Bu sayede bir object nesnesine her türden veri atanabilir. Örnek bir program:

 using System;
 class A
 {
 }
 class B:A
 {
 }
 class MainMetodu
 {
    static void Main()
    {
       A nesne1=new A();
       B nesne2=new B();
       nesne1=nesne2;
    }
 }

Bu programda gözden kaçırmamamız gereken nokta nesne1 üzerinden B sınıfının kendine özgü üye elemanlarına erişemeyeceğimizdir. Bu şekilde ana sınıf türünden bir nesnenin kullanılabildiği her yerde yavru sınıf türünden bir nesneyi de kullanabiliriz. Örneğin bu örneğimizi düşünecek olursak bir metodun parametresi A tipinden ise bu metoda parametre olarak B tipinden bir nesneyi de verebiliriz. Çünkü her B nesnesi A nesnesinin taşıdığı bütün üye elemanları taşır. Bunu bilinçsiz tür dönüşümüne benzetebiliriz.

object sınıfı[değiştir]

Şimdiye kadar bütün temel veri türlerinin object sınıfından türediğini söylemiştim. Aslında sadece temel veri türleri değil, bütün sınıflar gizlice object sınıfından türer. Bunu kanıtlamak için şu programı yazın:

using System;
class A
{
}
class Ana
{
   static void Main()
   {
      A nesne=new A();
      Console.WriteLine(nesne.ToString());
   }
}

ToString() metodu normalde object sınıfına aittir. Ancak bütün sınıf nesneleriyle kullanılabilir. Bu programda ekrana nesnenin türü olan A yazılır. Başka bir örnek:

using System;
class A
{
}
class Ana
{
   static void Main()
   {
      A nesne=new A();
      object a=nesne;
   }
}

Yine bu program da son derece hatasızdır.

Sanal metotlar[değiştir]

Sanal metotlar ana sınıf içinde bildirilmiş ve yavru sınıf içinde tekrar bildirilen metotlardır. Şimdilik bunun adı isim saklamadan başka bir şey değildir. Ancak bazı anahtar sözcükler ekleyerek bunun klasik bir isim saklama işleminden farklı olmasını sağlayacağız. Şimdi klasik şöyle bir program yazalım:

 using System;
 class A
 {
    public void Metot()
    {
       Console.WriteLine("A sınıfı");
    }
 }
 class B:A
 {
    new public void Metot()
    {
       Console.WriteLine("B sınıfı");
    }
    static void Main()
    {
       A nesneA=new A();
       B nesneB=new B();
       nesneA=nesneB;
       nesneA.Metot();
    }
 }

Bu program ekrana A sınıfı yazar. Çünkü nesneA nesnesi A sınıfı türündendir ve bu nesne üzerinden Metot() metoduna erişilirse A sınıfına ait Metot() metodu çalıştırılır. Şimdi böyle bir durumda B sınıfına ait Metot() metodunun çalıştırılmasını sağlayacağız.

using System;
class A
{
   virtual public void Metot()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   override public void Metot()
   {
      Console.WriteLine("B sınıfı");
   }
   static void Main()
   {
      A nesneA=new A();
      B nesneB=new B();
      nesneA=nesneB;
      nesneA.Metot();
   }
}

Bu program ekrana B sınıfı yazacaktır. Dikkat ettiyseniz bu programın öncekinden tek farkı A sınıfına ait metodun başına virtual anahtar sözcüğünün getirilmesi ve B sınıfına ait metodun başına da override anahtar sözcüğünün getirilmesi. Ana sınıftaki virtual anahtar sözcüğüyle ana sınıfa ait metodun bir sanal metot olmasını, yavru sınıftaki override anahtar sözcüğüyle de ana sınıfa ait aynı adlı sanal metodun görmezden gelinmesini sağladık. Bu sayede NesneA nesnesinin gizli türüne ait metot çalıştırıldı. Benzer mantıkla düşünecek olursak object a='r'; gibi bir ifadede a nesnesinin gizli türünün char olduğunu söyleyebiliriz. Bu konu hakkında bilmeniz gereken diğer şeyler:

  • Farkındaysanız burada bir nesnenin farklı türden bir nesne gibi davranması söz konusudur. Programlama jargonunda bunun adı çok biçimliliktir (polimorfizm).
  • Eğer ana sınıftaki metot virtual olarak bildirilmeseydi ve yavru sınıftaki metot override edilseydi programımız hata verip derlenmezdi.
  • Eğer yavru sınıftaki metot override olarak bildirilmeseydi ana sınıftaki metot çalıştırılırdı (program hata vermezdi).
  • Yavru sınıftaki override olarak belirtilen metotla ana sınıftaki virtual olarak bildirilen metot aynı isimli ve imzaları da aynı olmalıdır.
  • static metotlar virtual olarak bildirilemez. Şimdi örneğimizi biraz geliştirelim:
using System;
class A
{
   virtual public void Metot()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   override public void Metot()
   {
      Console.WriteLine("B sınıfı");
   }
}
class C:B
{
   public void Metot()
   {
      Console.WriteLine("C sınıfı");
   }
   static void Main()
   {
      A nesneA=new A();
      C nesneC=new C();
      nesneA=nesneC;
      nesneA.Metot();
   }
}

Bu program ekrana B sınıfı yazar. Çünkü C sınıfına ait metot override edilmediği için C'den önce gelen türeme zincirindeki son override edilen metot çalıştırılacaktır. Şimdi başka bir örnek yapalım:

using System;
class A
{
   virtual public void Metot()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   override public void Metot()
   {
      Console.WriteLine("B sınıfı");
   }
}
class C:B
{
   override public void Metot()
   {
      Console.WriteLine("C sınıfı");
   }
}
class D:C
{
   public new void Metot()
   {
      Console.WriteLine("D sınıfı");
   }
}
class E:D
{
   public new void Metot()
   {
      Console.WriteLine("E sınıfı");
   }
}
class F
{
   static void Main()
   {
      A a=new A();
      E e=new E();
      a=e;
      a.Metot();
   }
}

Bu örnekte ekrana C sınıfı yazılacaktır. override anahtar sözcüğü zaten bilinçli bir isim gizleme yapıldığını belirtir. Bu yüzden override olarak belirtilen bir üye elemanı tekrar new olarak belirtmek hatalıdır. Ayrıca bir türeme zincirindeki override döngüsü sondan delinebilir ama ortadan delinemez. Çünkü ortadan delinmeye çalışıldığında normal bir üye elemanı override etmemiz gerekir ki bunun yasak olduğunu daha önce söylemiştik.

Özet sınıf ve üye elemanlar[değiştir]

Özet sınıflar[değiştir]

Bazen bir ana sınıfın tek başına bir işlevi olmayabilir. Ana sınıfın tek görevi yavru sınıflara ait bazı ortak üye elemanları barındırmak olabilir ve ana sınıf türünden nesne yaratılmasını engellemek isteyebiliriz. İşte bu gibi durumlarda ana sınıf özet sınıf olarak bildirilir. Özet sınıfları bildirmek için abstract anahtar sözcüğü kullanılır. Örnek:

 using System;
 abstract class A
 {
    public string Metot()
    {
       return "Deneme";
    }
 }
 class B:A
 {
    static void Main()
    {
       B nesne1=new B();
       Console.WriteLine(nesne1.Metot());
       //A nesne2=new A();
       //Yukarıdaki satır olsaydı program hata verirdi.
    }
 }

NOT: Özet sınıflara statik veya statik olmayan yapıcı metot koyabiliriz. Bu durumda ilgili yapıcı metotlar sadece yavru sınıf türünden nesne yaratımlarında çalışacaktır.

Özet metotlar[değiştir]

Metotlar da, tıpkı sınıflar gibi özet olarak bildirilebilir. Özet metotlar her yavru sınıfta bulunması gereken, ancak içeriği bu yavru sınıf tarafından belirlenen metotlar oluşturmak için kullanılır. Bu sayede programcının bu üye elemanları unutarak es geçmesi engellenir. Örnek bir özet metot yaratımı:

abstract public void Metot();

Gördüğünüz gibi normal metot yaratım satırının başına bir de abstract anahtar sözcüğü ekleniyor. Metodun bloku yok, dolayısıyla da satır ; işareti ile sonlandırılıyor. Özet metotlarla ilgili bilmeniz gerekenler:

  • Özet metotlar yalnızca özet sınıfların içinde bildirilebilir.
  • Ana sınıfın içinde bildirilen özet metodu mutlaka, o sınıftan türeyen yavru sınıflar içinde override etmeliyiz.
  • Özet metotlar içsel olarak zaten sanal oldukları için tekrar virtual olarak bildirmek hatalıdır.
  • Elbette ki özet bir sınıf özet olmayan metot içerebilir.
  • Özet metotlar private olarak bildirilemez. Ya public ya da protected olmalıdırlar. Metodu yavru sınıfta uygularken de aynı erişim belirleyici kullanılmalıdır.
  • static metotlar özet olarak bildirilemez. Aynı şekilde özet metot bildiriminde virtual ve override anahtar sözcükleri kullanılamaz.

Özet özellikler[değiştir]

Özet özellikler, özet metotların taşıdığı bütün özellikleri taşırlar. Bunlara ek olarak şunları sayabiliriz:

  • Yalnızca sahte özellikler özet olarak bildirilebilir.
  • Ana sınıftaki özet sahte özelliği override eden yavru sınıftaki özelliğin set-get durumu ana sınıftaki özet özellikle aynı olmalıdır. Yani eğer özet özellikte sadece get bloku varsa bu özelliği override eden özelliğin de sadece get bloku olmalıdır. Aynı durum set bloku için de geçerlidir. Benzer şekilde özet özellikte hem set hem de get varsa override özellikte de bu ikisi olmalıdır. Örnek:
using System;
abstract class A
{
   abstract public int ozellik
   {
      set;
      get;
   }
}
class B:A
{
   override public int ozellik
   {
      get{return 100;}
      set{Console.WriteLine("Bu bir denemedir");}
   }
   static void Main()
   {
      B nesne=new B();
      Console.WriteLine(nesne.ozellik);
      nesne.ozellik=200;
   }
}

Gördüğünüz gibi özet sahte özelliğin set ve get blokları ayrı olarak yazılmıyor. set ve/veya get sözcüklerinin sonuna ; işareti koyuluyor.
NOT: abstract, virtual ve override üye elemanlar private olamaz.
NOT: Daha önce sanal metotları görmüştük. Sanal sahte özellikler de olabilir. Örnek:

using System;
class A
{
   virtual public int ozellik
   {
      set{}
      get{return 12;}
   }
}
class B:A
{
   override public int ozellik
   {
      get{return 100;}
      set{Console.WriteLine("Bu bir denemedir");}
   }
   static void Main()
   {
      B nesne=new B();
      A nesne2=new A();
      nesne2=nesne;
      Console.WriteLine(nesne2.ozellik);
      nesne2.ozellik=200;
   }
}

Bu program alt alta 100 ve Bu bir denemedir yazacaktır.
NOT: Sadece sahte özellikler virtual ve override olabilir. Normal özellikler virtual ve override olamaz.

NOT: Bir özet sınıftan başka bir özet sınıf türeyebilir. Bu durumda yavru sınıfın ana sınıftaki özet üye elemanları override etmesine gerek kalmaz. Ancak istenirse yine de override edebilir. Override etmesi durumunda yavru sınıftan türeyen özet olmayan torun sınıf, yavru sınıfın override ettiği üye elemanları tekrar override edebilir veya etmeyebilir. Ancak yavru sınıfın override etmediği ana sınıfın özet üye elemanlarının özet olmayan torun sınıf tarafından mutlaka override edilmesi gerekir. Ayrıca özet olmayan torun sınıfın, yavru sınıfın ana sınıftan ayrıca tanımladığı özet üye elemanları da override etmesi gerekir.

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

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

Bazen bir sınıftan türetilme yapılamamasını isteyebiliriz. İşte bu gibi durumlarda sealed anahtar sözcüğünü kullanabiliriz. Örnek:

 sealed class Sinif
 {
    ...
 }

Bir sealed sınıf yukarıdaki gibi bildirilir. Bu sınıf türetilemez.
NOT: abstract (özet) sınıflar sealed olarak işaretlenemez.
NOT: sealed sınıflara ek olarak static sınıflar ve yapılar da türetilmeyi desteklemez.

sealed metot ve sahte özellikler[değiştir]

Metot ve sahte özellikler de sealed olarak işaretlenebilir. sealed olarak işaretlenen metot ve sahte özellikler yavru sınıflarda override edilemez. Sadece override olarak işaretlenmiş metot ve sahte özellikler sealed olarak işaretlenebilir. Yeni üye eleman tanımlarken (anne sınıftan devralınmayan) elbetteki üye elemanı virtual olarak işaretlemeyerek bu üye elemanın override edilmesini engelleriz. Ancak zaten override ettiğimiz bir üye elemanın yavru sınıflarda tekrar override edilmesini engellemenin tek yolu ilgili üye elemanı sealed olarak işaretlemektir. Örnek:

class X
{
    protected virtual void F() { }
    protected virtual void F2() { }
}
class Y : X
{
    sealed protected override void F() { }
    protected override void F2() { }
}
class Z : Y
{
    // Aşağıdaki satır hata verir.
    // protected override void F() { }
    // Ancak aşağıdaki tanım geçerlidir.
    protected override void F2() { }
}

sealed olarak belirtilmiş üye elamana yavru sınıf halen new anahtar sözcüğü ile veya new anahtar sözcüğünü kullanmadan isim gizleme yapabilir. sealed anahtar sözcüğü ilgili üye elemanın sadece override edilmesini engeller. Yani sealed anahtar sözcüğü, yukarıdaki Y sınıfı türünden nesne üzerinden Y sınıfının üye elemanının çalışmasını garanti altına alır (gizli türü Z olsa bile).

Kalıtımla ilgili son notlar[değiştir]

Genel kural olarak yavru sınıftaki ana sınıftan devralınan elemanlar, ana sınıftan devralınan elemanları; yavru sınıfta isim gizleme yoluyla yeniden yazılan veya ana sınıfta bulunmayıp yavru sınıfta yazılan elemanlar yavru sınıftaki isim gizleyen elemanları kullanma eğilimindedir. Bu durumları örneklendirelim:

using System;
class A
{
   public void Metot1()
   {
      Metot2();
   }
   public void Metot2()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   public new void Metot2()
   {
      Console.WriteLine("B sınıfı");
   }
}
class Ana
{
   static void Main()
   {
      B b=new B();
      b.Metot1();
   }
}

Bu programda ekrana A sınıfı yazılır.

using System;
class A
{
   public void Metot1()
   {
      Metot2();
   }
   public void Metot2()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   public new void Metot1()
   {
      Metot2();
   }
   public new void Metot2()
   {
      Console.WriteLine("B sınıfı");
   }
}
class Ana
{
   static void Main()
   {
      B b=new B();
      b.Metot1();
   }
}

Bu programda da ekrana B sınıfı yazılacaktır. İstersek bu öncelikleri virtual ve override anahtar sözcükleriyle değiştirebiliriz. Şimdi birinci örneği şöyle değiştirelim:

using System;
class A
{
   public void Metot1()
   {
      Metot2();
   }
   virtual public void Metot2()
   {
      Console.WriteLine("A sınıfı");
   }
}
class B:A
{
   override public void Metot2()
   {
      Console.WriteLine("B sınıfı");
   }
}
class Ana
{
   static void Main()
   {
      B b=new B();
      b.Metot1();
   }
}

Bu örnekte ekrana B sınıfı yazılır. İkinci örnekteki durumu çözmek için base anahtar sözcüğü kullanılabilir. Ancak onu örnekleme gereksinimi görmüyorum. Çünkü daha önce görmüştük.

Özet geçmek gerekirse ana sınıftan devralınan elemanların da isim gizleyen elemanları kullanmasını istiyorsak ana sınıftaki elemanı virtual, yavru sınıftaki elemanı override olarak belirtiriz. Eğer çoklu türetme söz konusuysa anne sınıfta override edilmiş elemanı torun sınıfta tekrar override ederek nine sınıftan devralınan bir elemanın torun sınıftaki isim gizleyen elemanları kullanmasını sağlayabiliriz. Eğer torun sınıftaki elemanı override etmezsek torun sınıftaki nine sınıftan devralınan elemanlar anne sınıfın elemanını kullanacaktır.

NOT: Operatör metotları yavru sınıfta tekrar bildirilebilir. Ancak yavru sınıftaki operatör metodu tanımlanırken new anahtar sözcüğü kullanılamaz. Operatör metotları statik olmak zorunda olduğu için virtual, override veya abstract da olamazlar.

NOT: Statik üye elemanlara new anahtar sözcüğünü kullanarak veya kullanmadan isim gizleme yapılabilir.

NOT: new anahtar sözcüğüyle belirtilen her üye elemanın illaki ana sınıftaki bir üye elemanı gizlemesi gerekmez, ancak new anahtar sözcüğünün bu şekilde kullanılması gereksizdir ve derleyici uyarı verir.

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