C Sharp Programlama Dili/Operatör aşırı yükleme

Şimdiye kadar kendi sınıflarımızı, metotlarımızı, özelliklerimizi yazdık. Şimdi kendi operatörlerimizi yazmaya sıra geldi. Şimdi içinde saat bilgileri olan bir sınıf tasarlayacağız:

 using System;
 class Zaman
 {
    public int Saat;
    public int Dakika;
    public int Saniye;
    public Zaman(int saat, int dakika, int saniye)
    {
        Dakika=dakika+saniye/60;
        Saniye=saniye%60;
        Saat=saat+Dakika/60;
        Dakika=Dakika%60;
    }
 }

Gördüğünüz gibi Zaman sınıfının Saat, Dakika ve Saniye olmak üzere üç özelliği var ve bu özellikler nesne yaratılırken girilmek zorunda. Dakika ve saniye 60’tan büyük olamayacağı için yapıcı metotta gerekli düzenlemeler yapılıyor. Bu sınıftan bir nesne yarattığımızda söz konusu nesnenin Saat, Dakika ve Saniye olmak üzere üç özelliği olacak. Başka bir nesne yarattığımızda da aynı şekilde bunun da Saat, Dakika ve Saniyesi olacak. Peki Zaman türünden iki nesnenin + operatörüne sokularak bu iki zaman diliminin toplanmasını ister miydiniz? Örneğin zaman1 ve zaman2 Zaman türünden iki nesne olsun. zaman1+zaman2 yazdığımızda karşılıklı dakika ve saniyeler toplansın ve saniyeden dakikaya ve dakikadan saate gerekli aktarımlar yapılsın. İşte şimdi bunu yapacağız:

 using System;
 class Zaman
 {
    public int Saat;
    public int Dakika;
    public int Saniye;
    public Zaman(int saat, int dakika, int saniye)
    {
        Dakika=dakika+saniye/60;
        Saniye=saniye%60;
        Saat=saat+Dakika/60;
        Dakika=Dakika%60;
    }
    public static Zaman operator+(Zaman a, Zaman b)
    {
        int ToplamSaniye=a.Saniye+b.Saniye;
        int ToplamDakika=a.Dakika+b.Dakika;
        int ToplamSaat=a.Saat+b.Saat;
        return new Zaman(ToplamSaat,ToplamDakika,ToplamSaniye);
    }
 }        
 class AnaProgram
 {
    static void Main()
    {
       Zaman zaman1=new Zaman(5,59,60);
       Zaman zaman2=new Zaman(1,0,120);
       Zaman zaman3=zaman1+zaman2;
       Console.WriteLine("{0}.{1}.{2}",zaman3.Saat,zaman3.Dakika,zaman3.Saniye);
    }
 }

Bu program ekrana 7.2.0 yazacaktır. Gördüğünüz gibi kendi operatörlerimizi yazmak, daha doğrusu + operatörüne aşırı yükleme yapmak çok da zor değil. Operatör metotlarımızın adı operator+ mantığında olmalı. Örneğin söz konusu operatörümüz – olsaydı operatör metodumuzun adı operator- olacaktı. Operatörümüzün alacağı operandları bu metoda parametre olarak gönderiyoruz. Operatör metodunun tuttuğu değer operatörün operandlarıyla birlikte tuttuğu değeri ifade eder. return new Zaman(ToplamSaat,ToplamDakika,ToplamSaniye); satırıyla metodun, dolayısıyla da operatörün verilen parametreler ölçüsünde Zaman türünden bir nesne tutmasını sağladık. Ancak şimdi Zaman türünden bir nesneyle int türünden bir nesneyi toplamamız mümkün değildir. Eğer Zaman türünden bir nesneyle int türünden bir nesne toplanmak istenirse int türünden nesneyi saniye olarak hesaba katabiliriz. Bunun içinse Zaman sınıfına şu operatör metodunu ekleyebiliriz:

 public static Zaman operator+(Zaman a, int b)
 {
   int ToplamSaniye=a.Saniye+b;
   return new Zaman(a.Saat,a.Dakika,ToplamSaniye);
 }

Gördüğünüz gibi bu kadar basit. Ayrıca şunu hatırlatmak isterim: return new Zaman(a.Saat,a.Dakika,ToplamSaniye) satırıyla metodun Zaman türünden verilen parametrelere göre yeni bir nesne oluşturulup bu nesneyi tutmasını sağladık. Dolayısıyla Zaman sınıfının yapıcı metodu çalıştırıldı. Dolayısıyla da bizim tekrar Saniye, Dakika ve Saat arasında aktarım yapmamıza gerek kalmadı. Operatör metotlarıyla ilgili bilmeniz gerekenler:

  • Operatör metotlarının geri dönüş tipinin illaki söz konusu sınıf tipinden olmasına gerek yoktur. Örneğin Zaman sınıfındaki ilk toplama operatör metodunu şöyle değiştirebiliriz.
 public static string operator+(Zaman a, Zaman b)
 {
     int ToplamSaniye=a.Saniye+b.Saniye;
     int ToplamDakika=a.Dakika+b.Dakika;
     int ToplamSaat=a.Saat+b.Saat;
     Zaman nesne=new Zaman(ToplamSaat,ToplamDakika,ToplamSaniye);
     return nesne.Saat+"."+nesne.Dakika+"."+nesne.Saniye;
 }

Burada Zaman nesne=new Zaman(ToplamSaat,ToplamDakika,ToplamSaniye); satırını ekleyerek Saat, Dakika, Saniye aktarımlarının yapılmasını sağladık. Bu metoda göre Zaman tipinden a ve b nesneleriyle yapılan a+b ifadesinin sonucu string tipinde olur.

  • Operatör metotları static ve public olmalıdır.
  • Dört işlem operatörleri (+, -, *, /) herhangi bir koşul olmaksızın aşırı yüklenebilirler.

İlişkisel operatörlerin aşırı yüklenmesi

Şimdi Zaman sınıfına aşağıdaki operatör metotlarını ekleyelim.

 public static bool operator==(Zaman a, Zaman b)
 {
     if(a.Saniye==b.Saniye&&a.Dakika==b.Dakika&&a.Saat==b.Saat)
         return true;
     else
        return false;
 }
 public static bool operator!=(Zaman a, Zaman b)
 {
     return !(a==b);
 }

İlişkisel operatörlerdeki ana kural ilişkisel operatörlerin birbirlerinin zıtlarının sınıf içinde ve aynı türde olmasının zorunlu olmasıdır. Yani biz burada yukarıdaki metotların yalnızca bir tanesini yazıp bırakamazdık ya da birininin geri dönüş tipini bool, birinin int yapamazdık. Ayrıca return !(a==b); satırı son derece mümkündür. Burada daha önce yazılmış bir metodu kullandık. Her iki metot da static olduğu için herhangi bir nesne tanımlamaya gerek yoktur. İlişkisel operatörlerin geri dönüş tipi bütün türlerde olabilmesine rağmen bool olması tavsiye edilir. Diğer ilişkisel operatörler olan <, >, <= ve >= operatörlerini de çift hâlinde olmak şartıyla tıpkı örneğimizdeki gibi aşırı yükleyebiliriz.

true ve false operatörlerinin aşırı yüklenmesi

Şimdiye kadar true ve falseu bool türünden sabitler olarak gördük. Ancak şimdi bu true ve falseu operatörmüş gibi aşırı yükleyebileceğiz. Şimdi şu metotları Zaman sınıfına ekleyin:

 public static bool operator true(Zaman a)
 {
     if(a.Saat>12)
         return true;
     else
        return false;
 }
 public static bool operator false(Zaman a)
 {
    if(a.Saat<=12)
        return true;
    else
        return false;
 }

Bu metotları asıl programımız içinde şöyle kullanabiliriz:

 Zaman zaman1=new Zaman(5,59,60);
 Zaman zaman2=new Zaman(2,35,40);
 if(zaman1)
    Console.WriteLine("Öğleden sonra");
 else
    Console.WriteLine("Öğleden önce");
 if(zaman2)
    Console.WriteLine("Öğleden sonra");
 else
    Console.WriteLine("Öğleden önce");

Gördüğünüz gibi derleyici normalde true ya da false olması gereken bir yerde Zaman türünden bir nesne gördüğü zaman Zaman sınıfındaki true ve false metotlarını çalıştırıyor ve söz konusu Zaman nesnenisinin true ya da false değerlerden birisini tutmasını sağlıyor. Eğer böyle bir yapı oluşturmak istiyorsak hem true hem de false metotlarının sınıfımız içinde bulunması gerekir. Ayrıca true ve false operatör metotlarının geri dönüş tipi mutlaka bool türünden olmalıdır.

Mantıksal operatörlerin aşırı yüklenmesi

Örnek (sınıfımıza eklenecek):

 public static bool operator|(Zaman a,Zaman b)
 {
    if(a.Saat>12||b.Saat>12)
       return true;
    else
       return false;
 }

& ve |, ! operatörlerinin aşırı yüklenebilmesi için herhangi bir şart yoktur. (! operatörünün tek operand aldığını unutmayın.) Ancak && ve || operatörlerinin aşırı yüklenebilmesi için söz konusu sınıf için şu şartların sağlanması gerekir:

  • & ve | operatörleri aşırı yüklenmiş olmalıdır.
  • true ve false operatörleri aşırı yüklenmiş olmalıdır.
  • operator& ve operator| operatörlerinin parametreleri ilgili sınıf türünden olmalıdır.
  • operator& ve operator| operatörlerinin geri dönüş tipi ilgili sınıf türünden olmalıdır.
  • Yukarıdaki şartlar sağlandığı takdirde bizim ayrıca && ve || operatörlerini aşırı yüklememize gerek kalmaz. Yukarıdaki şartlar sağlandığı takdirde sanki && ve || operatörleri aşırı yüklenmiş gibi kodumuzu yazabiliriz. Örnek:
 using System;
 class Sinif
 {
    public int Sayi;
    public Sinif(int sayi)
    {
       Sayi=sayi;
    }
    public static bool operator true(Sinif a)
    {
       return true;
    }
    public static bool operator false(Sinif a)
    {
       return false;
    }
    public static Sinif operator&(Sinif a,Sinif b)
    {
       return new Sinif(20);
    }
    public static Sinif operator|(Sinif a,Sinif b)
    {
       return new Sinif(30);
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Sinif a=new Sinif(50);
       Sinif b=new Sinif(10);
       Console.WriteLine((a||b).Sayi);
       Console.WriteLine((a&&b).Sayi);
    }
 }

Yukarıdaki programda ekrana alt alta 50 ve 20 yazar. Peki neden 50 ve 20 yazdı? İşte mantığı:

true operatörü false operatörü a||b a&&b
true false ilk operand & operatör metodu
false true | operatör metodu ilk operand
true true ilk operand ilk operand
false false | operatör metodu & operatör metodu

Tablodan da görebileceğiniz gibi örneğimizde true operatörü true, false operatörü false değer ürettiği için a||b ifadesi ilk operand olan a’yı, a&&b ifadesi ise & operatör metodunun geri dönüş değerini tutar.

[düzenle] Tür dönüşüm operatörünün aşırı yüklenmesi

Hatırlarsanız C#’ta iki tane tür dönüşüm mekanizması vardı. Bunlardan birincisi bilinçli, ötekisi de bilinçsiz tür dönüşümüydü. Şimdi bu iki olayın kendi sınıflarımız türünden nesneler için de gerçekleşmesini sağlayacağız. Bu iki olayın taslağı şu şekildedir:

 public static implicit operator HedefTur(KaynakTurdekiNesne)
 {
    return HedefTurdenVeri;
 }

Bu bilinçsiz tür dönüşümü içindi. Şimdi de bilinçli tür dönüşümü metodunun taslağı:

 public static explicit operator HedefTur(KaynakTurdekiNesne)
 {
    return HedefTurdenVeri;
 }

Şimdi bu taslakları örneklendirelim:

 using System;
 class Sinif
 {
    public int Sayi;
    public Sinif(int sayi)
    {
       Sayi=sayi;
    }
    public static implicit operator int(Sinif a)
    {
       return a.Sayi;
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Sinif a=new Sinif(50);
       int b=a;
       Console.WriteLine(b);
    }
 }

Şimdi de bilinçli tür dönüşümünü örneklendirelim:

 using System;
 class Sinif
 {
    public int Sayi;
    public Sinif(int sayi)
    {
       Sayi=sayi;
    }
    public static explicit operator int(Sinif a)
    {
       return a.Sayi;
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Sinif a=new Sinif(50);
       int b=(int)a;
       Console.WriteLine(b);
    }
 }

Gördüğünüz gibi bilinçli ve bilinçsiz tür dönüşümü operatör metotlarını yazarken tek değişen implicit ve explicit anahtar sözcükleri. Bir sınıf içinde parametre ve geri dönüş tipi aynı olan explicit ve implicit metotlar aynı anda bildirilemez. Eğer implicit metodu bildirmişsek explicit (bilinçli) tür dönüşümüne de izin vermiş oluruz. İlk örneğimizde (implicit) a nesnesini int türünden değişkenlerin kullanılabildiği her yerde kullanabiliriz. İkinci örneğimizde ise aynı şeyi tür dönüştürme operatörünü kullanarak yapabiliriz. implicit ve explicit metotlarla yalnızca Sinif türünden başka türlere ya da başka türlerden Sinif türüne dönüşüm yapabiliriz. Yani örneğin byte ile int arasındaki dönüşüm mekanizmasına müdahale edemeyiz.

BİLGİLENDİRME: Şimdi konunun başında gördüğümüz Zaman sınıfını düşünün. Artık implicit metot sayesinde Zaman türünden bir nesneye a=”12.45.34″; şeklinde değer atayabilirsiniz. Bunun için Zaman sınıfının implicit metodu içinde gerekli parçalamaları yapar ve sonucu bir Zaman nesnesinde tutarsınız. Gördüğünüz gibi adeta kendi sabitlerimizi yazabiliyoruz. Bunun gibi daha birçok şey hayal gücünüze kalmış.

NOT: Artık bu öğrendiğimiz implicit metot sayesinde Sinif türünden bir nesneyi new anahtar sözcüğünü kullanmadan direkt Sinif a=23; gibi bir şey yazarak oluşturabiliriz. Ancak bunun için tabii ki sınıfımız içinde int türünden Sinif türüne implicit dönüşüm metodunu oluşturmamız gerekir.

NOT: Bildiğiniz gibi C#’ta bazı türler arasında bilinçsiz tür dönüşümü olur. Örneğin C#’ta byte türünden inte bilinçsiz tür dönüşümü mümkündür. Ancak biz programımızı yazarken bunları kafamıza takmamıza gerek yok. Yani Sinif türünden int’e dönüşüm yapan metot oluşturmuşsak Sinif türünden byte’a dönüşüm yapan metot oluşturamayız diye bir kural yoktur. İstediğimiz gibi tür dönüşüm metotlarını yazabiliriz. Yeter ki bir türden bir türe dönüşüm yapan birden fazla metot oluşturmayalım ve dönüşümün taraflarından bir tanesi Sinif türünden olsun.

Operatör aşırı yüklemeyle ilgili son notlar

Atama operatörünü (=) aşırı yükleyemeyiz. Çünkü zaten gerek yoktur. Biz atama operatörünü aşırı yükleyelim veya yüklemeyelim zaten kullanabiliriz. İşlemli atama operatörlerinde ise şöyle bir kural vardır: Örneğin + operatörünü aşırı yüklemişsek += operatörünü de kullanabiliriz. Bu durum bütün işlemli atama operatörlerinde geçerlidir. Hiçbir operatörün öncelik sırasını değiştiremeyeceğimiz gibi temel veri türleri (string, int, vb.) arasındaki operatörlere de müdahale edemeyiz. Bu konuda işlenenler dışında hiçbir operatörü aşırı yükleyemeyiz. Örneğin dizilerin elemanlarına erişmek için kullanılan [] operatörünü, yeni bir nesneye bellekte yer açmak için kullanılan new operatörünü, ?: operatörünü, vb. aşırı yükleyemeyiz.

About mehmetakifsonmez

Tokat Teknik ve Endüstri Meslek Lisesi - Bilişim Teknolojileri Alanı Şefi
Bu yazı C# Temel Seviye içinde yayınlandı. Kalıcı bağlantıyı yer imlerinize ekleyin.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s