20
Şub'14

OOP 8: Base Ctor Çağırımları, Base Ctor’a Parametre Geçirme, Base, Override, New Anahtar Kelimeleri (C# ile OOP)

Bu yazımda kalıtıma kısa bir giriş yaparak, kalıtım uygulanmış bir yapıda ctor çağırımlarının hangi sırada gerçekleştiğini, base ctor’a nasıl parametre gönderildiğini, bazı anahtar kelimelerin(base, override, new) ne işe yaradıklarını ve kullanımlarını açıklamaya çalışacağım.

 

Kalıtım hakkında bilgi sahibi olmak için bir önceki yazım olan OOP 7‘yi okuyabilirsiniz. Eğer hazırsak kalıtıma kısa bir giriş ile başlayalım.

 

Girişte bahsettiğim konulara açıklık getirebilmek için elbette kalıtım uygulanmış bir yapıya ihtiyacım var. Eşya sınıfından türetilmiş elektronik eşya sınfı ve elektronik eşya sınıfından türetilen iki adet sınıfımız(telefon, bilgisayar) olsun.

Class View

Kalıtım Uygulanmış Sınıflarda Ctor Çağrım Sırası

Bu sınıfların her biri içerisine bir ctor kodlayalım ve her ctor, çağırıldığında kim olduğunu bize söylüyor olsun. Son durumda kodladığımız sınıfların içerisi şu şekildedir:

class Esya
{
    public Esya()
    {
        Console.WriteLine("Eşya sınıfının yapıcı fonksiyonu çağırıldı.");
    }
}

class ElektronikEsya : Esya
{
    public ElektronikEsya()
    {
        Console.WriteLine("ElektronikEsya sınıfının yapıcı fonksiyonu çağırıldı.");
    }
}

class Telefon : ElektronikEsya
{
    public Telefon()
    {
        Console.WriteLine("Telefon sınıfının yapıcı fonksiyonu çağırıldı.");
    }   
}

class Bilgisayar : ElektronikEsya
{
    public Bilgisayar()
    {
        Console.WriteLine("Bilgisayar sınıfının yapıcı fonksiyonu çağırıldı.");
    }
}

Main class içerisinde de Telefon sınıfından bir nesne yaratarak ctor çağırımlarının sırasını inceleyelim.

static void Main(string[] args)
{
    Telefon telefon = new Telefon();
}

Program çalıştırıldığında aşağıdaki çıktı alınır.

CtorCagirimiSirasi

Gördüğünüz gibi ilk önce kalıtım ağacının en tepesindeki sınıfın yapıcı fonksiyonu çağrıldı. Ardından sırayla child class’lara doğru inilerek base class yapıcı fonksiyonları sıra ile çağrıldı.

Base Ctor’a Parametre Gönderilmesi

Base ctor’a parametre göndermek için base anahtar kelimesi kullanılır. Bu anahtar kelimeyi sınıfımızın ctor tanımının yanına “:” koyarak yazarız ve parantezler arasına base sınıfa göndereceğimiz parametleri belirtiriz.

class BaseClassName
{
    public BaseClassName(int parametre1)
    {

    }
}

// Base class ctor'u integer bir parametre alıyor.
class ChildClassName : BaseClassName
{
    public ChildClassName (int parametre1, string parametre2)
        : base(parametre1) // parametre1 base ctor'a gönderildi.
    {

    }
}

Bu konuya açıklık getirebilmek için ilk örnek üzerinden sınıfların ctor yönlendirmelerini yaparak devam edelim.

class Esya
{
    public Esya(string esyaAdi)
    {
        Console.WriteLine(esyaAdi);
    }
}

class ElektronikEsya : Esya
{
    public ElektronikEsya(int voltaj, string esyaAdi)
        :base(esyaAdi)
    {
        Console.WriteLine(voltaj);
    }
}

class Bilgisayar : ElektronikEsya
{
    public Bilgisayar(string ekranKartiMarkasi, int voltaj, string esyaAdi)
        :base(voltaj,esyaAdi)
    {
        Console.WriteLine(ekranKartiMarkasi);
    }
}

Bir bilgisayar nesnesi yaratarak ctor yönlendirmelerinin doğru şekilde çalışıp çalışmadığına bakalım.

static void Main(string[] args)
{
    Bilgisayar bilgisayar = new Bilgisayar("NVIDIA", 18, "Bilgisyar");
}

Ekran görüntüsü aşağıdaki gibidir:

CtorYonlendirmeleriSonuc

Kısa bir özet:

– Kalıtım yapıldığında ctor çağırımlarının hangi sırada olduğunu ve base ctor’a nasıl parametre geçirildiğini yani ctor yönlendirmelerini inceledir.

– Bir sınıfın base sınıfının tek ctor’u varsa ve parametre alıyorsa kalıtılan sınıf base ctor’a parametre göndermek zorundadır.

– Kalıtılan sınıf(child sınıf) ctor’u hiç parametre almıyor da olabilir. Fakat base class ctor’u parametre alıyorsa yine bu parametreyi kalıtılan sınıftan base sınıfa yukarıda açıkladığımız gibi ctor yönlendirmesi ile geçirmemiz gerekecektir.

Base Anahtar Kelimesi

Ctor yönlendirmelerinde base anahtar kelimesini kullandık. Şimdi bu anahtar kelimeye biraz daha ayrıntılı olarak değinelim ve tam olarak ne anlama geldiğinden ve başka nasıl kullanabileceğimizden bahsedelim.

 

Base adından da anlaşılabileceği gibi bir üst sınıfı yani içinde bulunulan sınıfın türetildiği ebeveyn sınıfı ifade etmektedir.Hatırlayacaksınız ki this anahtar kelimesi ile içinde bulunulan sınıfı ifade ediyorduk.

 

Base anahtar kelimesi ile bir üst sınıfın tüm public ve protected üyelerine erişebiliriz. Eğer bizim belirttiğimiz bir base class yoksa da Object sınıfının base olacağını unutmayalım.

 

Tabiki base class üyelerini child sınıfta kullanırken base.UyeAdi gibi belirtmek zorunda değiliz. UyeAdi da desek derleyici doğru property’i set edecektir. This anahtar kelimesinde olduğu gibi base anahtar kelimesini de kullanmamanın gerçekten karışıklık yaratacağı durumlar olabilir. Metotlara aldığınız parametreler ile sınıftaki field ya da property’lerin aynı isimlerde olmamasına dikkat edin. Base ve this anahtar kelimesinin, daha anlaşılır kodlar yazmak için mümkün olduğunca kullanılmasından yanayım.

class Telefon : ElektronikEsya
{
    public Telefon(int voltaj, string esyaAdi)
        : base(voltaj, esyaAdi)
    {
        Console.WriteLine(base.GetType());
    }
}

Yukarıdaki örnekte base anahtar kelimesini kullanarak, object sınıfı içerisinde bulunan GetType() metodunu çağırdık. Sadece GetType() de yazsak aynı sonucu alacaktık. Telefon sınıfı ElektronikEsya’dan kalıtıldı diye base anahtar kelimesi sadece ElektronikEsya sınıfı üyelerini görmemizi sağlar diye düşünmeyin. Tüm base sınıfların, görmemize izin verilen üyelerini base. diyerek görebiliriz. Bu arada GetType() metodu kendini çağıran tipi döndürür. Telefon sınıfından bir nesne örneklerseniz ekranda NamespaceAdi.Telefon göreceksiniz. Telefondan türetilen AkilliTelefon adli bir sınıf olsaydı ve biz AkilliTelefon’dan nesne örnekleseydik ekrana yazılacak tip Telefon değil, AkilliTelefon olacaktı. Dikkat edin, GetType() için kendini çağıran tipi döndürür demiştik.

Override Anahtar Kelimesi

Base classta bir metod yazıyoruz ve bu sınıftan kalıtılan tüm sınıflar için bu metodu kullanabiliyoruz. Örneğin object sınıfına ait ToString() metodunu çoğumuz defalarca kullanmışızdır. Aşağıdaki sınıf için ToString() metodunu çağırıp sonuca bakalım.

class Esya
{
    public string EsyaAdi { get; set; }
    public decimal Fiyat { get; set; }
}
static void Main(string[] args)
{
    Esya esya = new Esya();

    Console.WriteLine(esya.ToString());
}

objectTostring

Esya sınıfının bir ToString() metodu olmadığı halde bu metodu çağırabildik. Çünkü biz belirtmesekte Esya sınıfı object sınıfından kalıtılmıştır ve burada çağırdığımız ToString() metodu object sınıfına aittir. Object sınıfının ToString() metodu bize “KalitimPlus.Esya” diye bir çıktı verdi. ToString(), birçok tip için istediğimiz sonucu veriyor olabilir fakat biz eşya nesnesinin adını ve fiyatını göstermesini istiyor olabiliriz. İşte bu noktada ihtiyacımız olan şey override anahtar kelimesi ve unutmamamız gereken bir durum var ki bir metodu override edebilmemiz için base class’ta bu metod virtual olarak işaretlenmiş olmalıdır. Abstract sınıfları açıklamaya çalışırken virtual anahtar kelimesine de değiniyor olacağım.

class Esya
{
    public string EsyaAdi { get; set; }
    public decimal Fiyat { get; set; }

    public override string ToString()
    {
        return EsyaAdi + ": " + Fiyat + " TL";
    }
}
static void Main(string[] args)
{
    Esya esya = new Esya() { EsyaAdi = "Pc", Fiyat = 999 };

    Console.WriteLine(esya.ToString());
}

overrideTostring

Artık ToString() istediğimiz değeri veriyor. Fakat hiç override anahtar kelimesini kullanmasak yine aynı sonucu alabilirdik. Sadece bir uyarı veren derleyici kodu yine derleyecekti. Peki override kullanmak burada nasıl bir farklılık yarattı? Bunu yazının devamında new anahtar kelimesini(Member Hiding) de anlattıktan sonra açıklamaya çalışacağım.

New Anahtar Kelimesi (Member Hiding)

New ile override birlikte düşünülmesi gereken iki anahtar kelimedir. Base class metodlarını ezmek için override anahtar kelimesini kullanıyorken, base class metodunu ezmek istemiyorsak new anahtar kelimesini kullanabiliriz. Böyle bir kullanım member hiding olarak isimlendiriliyor.

 

New ya da override kullanmadan child class’a base class’ın metodu ile aynı isimli bir metod kodlamaya kalkışabiliriz. Bu durumda derleyici yalnızca bir uyarı verip bu anahtar kelimeleri kullanmamız yönünde öneride bulunacaktır. Peki bu durumda biz override ya da new kullanmazsak program hangi yönde davranış gösterecek? İsterseniz ilk önce override ve new kullanımı arasındaki davranış farkına bakalım ve sonrasında bu anahtar kelimeler kullanılmadığındaki davranışı inceleyelim.

 

Bir çokgen sınıfımız olsun ve bundan türetilen bir kare sınıfı. Çokgen sınıfı içerisinde kenar uzunluklarını tutan integer bir dizi ve en uzun kenar bulmaya yarayan public bir metod olsun. Kare sınıfı ise bir kenarının uzunluğunu parametre olarak alan ve kenar uzunluklarını set eden bir constructor’a sahip olsun.

class Cokgen
{
    public int[] KenarUzunluklari { get; set; }

    public int EnUzunKenarBul()
    {
        Console.WriteLine("Cokgen: EnUzunKenarBul() çağırıldı.");

        int enUzunKenar = 0;

        if (this.KenarUzunluklari.Length == 0) return 0;

        for (int i = 0; i < this.KenarUzunluklari.Length; i++)         
        {             
            if (this.KenarUzunluklari[i] > enUzunKenar)
            {
                enUzunKenar = this.KenarUzunluklari[i];
            }
        }

        return enUzunKenar;
    }
}

class Kare : Cokgen
{
    public Kare(int a)
    {
        base.KenarUzunluklari = new int[] { a,a,a,a };
    }
}

Bu durumda bir kare nesnesi örnekleyip en uzun kenarını göstermek istediğimizde aşağıdaki gibi bir kod yazabiliriz.

static void Main(string[] args)
{
    Kare kare = new Kare(15);
    Console.WriteLine(kare.EnUzunKenarBul());
}

Aşağıdaki şekilde bir çıktı aldık:

oopEnUzunKenar

Şimdi çokgen sınıfı içerisindeki en uzun kenar bulma metodu genel bir amaçla yazılmış ve herhangi bir çokgenin kenar uzunlukları tanımlandığı taktirde en uzun kenarı bulabilir. Kare sınıfı için biz daha basit şekilde bu işi halledebiliriz. Öyleyse kare sınıfında bu metodu, new anahtar kelimesini de kullanarak basit bir şekilde kodlayalım. Kare sınıfının son hali aşağıdaki gibi olacaktır.

class Kare : Cokgen
{
    public Kare(int a)
    {
        base.KenarUzunluklari = new int[] { a,a,a,a };
    }

    public new int EnUzunKenarBul()
    {
        Console.WriteLine("Kare: EnUzunKenarBul() çağırıldı.");  

        if (base.KenarUzunluklari.Length > 0)
        {
            return base.KenarUzunluklari[0];
        }
        else
        {
            return 0;
        }
    }
}

Karenin tüm kenarları aynı uzunlukta olduğu için sadece bir kenarının uzunluğunu söylemek yeterlidir en uzun kenar için. Kare sınıfını bu şekilde kodladıktan sonra çıktıyı tekrar incelersek:

oopKareEnUzunKenar

Gördüğünüz gibi bu sefer sadece kare sınıfının ilgili metodu çağrıldı. İyide biz aynı sonucu override anahtar kelimesini kullanınca da almıştık. O zaman şimdi override kullanmak ile new kullanmak arasındaki farkı görebileceğimiz şekilde main fonksiyonunun içerisini düzenleyelim.

static void Main(string[] args)
{
    Kare kare = new Kare(15);
    Console.WriteLine(kare.EnUzunKenarBul());

    Cokgen cokgenKare = new Kare(25);
    Console.WriteLine(cokgenKare.EnUzunKenarBul());
}

Çıktı ise aşağıdaki gibidir:

cokgenNewKare

Oda nesi! Çokgen tipinden yeni bir kare örneklememize rağmen karenin en uzun kenar bulma metodu çağrılmadı. İşte member hiding’den kastımız bu. O zaman şimdi kare sınıfı içerisindeki en uzun kenar bulma metodunu new ile değil override ile yazmış olalım. Override edebilmek için metodun base class’ta virtual olarak işaretlenmesi gerektiğini unutmayalım.

class Cokgen
{
    public int[] KenarUzunluklari { get; set; }

    public virtual int EnUzunKenarBul()
    {
    ...
    ...

class Kare : Cokgen
{
    public Kare(int a)
    {
        base.KenarUzunluklari = new int[] { a,a,a,a };
    }

    public override int EnUzunKenarBul()
    {
    ...
    ...

Çıktı aşağıdaki gibi olacaktır:

overrideKare

Bu sefer ise yine çokgen tipinden bir kare örneklememize rağmen kare sınıfının en uzun kenar bulma metodu çağrıldı. Peki başta dediğimiz gibi ne override ne de new anahtar kelimesi kullanmasaydık çıktı nasıl olurdu beraber deneyerek bakalım…

notOverrideNotNew

Ekran alıntısını incelediğinizde, derleyicinin metodun altını çizerek uyarı verdiğini fakat yine de kodu derleyerek new anahtar kelimesi kullanmışız gibi davrandığını görebilirsiniz. Ayrıca böyle bir durumda base class’taki en uzun kenar bulma metodunu virtual yapıp yapmamanız sonucu değiştiren bir etmen değildir.

 

Özet

 

– Ctor çağırımlarında, ilk önce en tepedeki sınıfın ctor’u çağırılır ve sırayla base class’lara doğru inilir.

– Base ctor’a :base(parametre1,parametre2, …) formatında bir kullanım ile parametre gönderilebilir.

– Base anahtar kelimesi base.UyeAdi şeklinde kullanılabilir ve burada base, tüm ebeveyn sınıfları ifade eder. base. dendiğinde intelligence bize tüm base class’lardaki ulaşma iznimiz olan elemanları gösterecektir.

Override anahtar kelimesi base class’taki metodu ezmemizi sağlar. Tabiki bunu yapabilmemiz için base class’taki bu metod, virtual olmalıdır.

New anahtar kelimesi ile override’a benzer bir iş yaparız fakat aradaki fark member hiding‘dir. New anahtar kelimesi ile member hiding yapıldığında, ilgili metod ancak kendi sınıfının tipinden bir nesne tarafından çağırılırsa aktif rol alacaktır.

 

Not: Cokgen sınıfının abstract olması yukarıdaki örnek için daha mantıklıydı. Abstract konusuna ileride değineceğim. İsterseniz nedir ne değildir bir araştırın derim:)

 

Uzun soluklu bir yazı oldu. Faydasını görmeniz umuduyla…

 

İyi Eğlenceler:)

Yeni makaleleri E-Mail ile takip edin!