C Sharp Programlama Dili/Temsilciler
Şimdiye kadar öğrendiklerimiz türler sınıflar, yapılar, enumlar ve arayüzlerdi. Şimdi temsilci isminde yeni bir tür daha öğreneceğiz. Temsilcilerin nesneleri oluşturulabilir ve bu nesneler metotları temsil ederler. Temsilci bildirimi delegate
anahtar sözcüğü ile yapılır. Bir temsilci bildirim taslağı şu şekildedir:
delegate GeriDönüşTipi Temsilciİsmi(parametreler);
Bu taslağa göre örnek bir temsilci bildirimi şöyle yapılır:
delegate int temsilci(int a,string b);
Temsilcilerin bir tür olduğunu ve tür tanımlarının sınıf veya isim uzayı kapsamında yapıldığını, bu yüzden metot içinde tanımlayamayacağımızı tekrar hatırlayalım. Temsilciler erişim belirleyicisi alabilir. Temsilciler genellikle programlarımıza belirli bir ifadeyle birden fazla metodu çağırabilme yeteneği vermek için kullanılır. Örnek program:
using System;
class deneme
{
delegate void temsilci();
static void Metot1()
{
Console.WriteLine("Burası Metot1()");
}
static void Metot2()
{
Console.WriteLine("Burası Metot2()");
}
static void Main()
{
temsilci nesne=new temsilci(Metot1);
nesne();
nesne=new temsilci(Metot2);
nesne();
}
}
Bu program ekrana alt alta Burası Metot1()
ve Burası Metot2()
yazacaktır. Yani bir sınıf nesnesi oluşturur gibi bir temsilci nesnesi oluşturuyoruz. Yapıcı metoda parametre verir gibi temsilci nesnesinin hangi metodu temsil edeceğini belirtiyoruz. Sonra bu temsilci nesnesi üzerinden temsil edilen metodu çalıştırıyoruz. Daha sonra temsilci nesnesinin değerini yani hangi metodu temsil ettiğini değiştirebiliyoruz. Burada dikkat etmemiz gereken nokta temsilci nesnesinin geri dönüş tipi ve parametre olarak kendiyle uyumlu metodu temsil edebileceğidir. Erişim belirleyicisi, static olup olmama durumu, vs. bu uyuma dâhil değildir. Bu örneğimizde parametre almayan ve geri dönüş tipi void olan temsilcimiz her iki metodu da temsil edebildi.
NOT: Temsilciler sınıfların taşıdığı özelliklerin bazılarını taşırlar. Örneğin bir metot parametre olarak bir temsilci tipinden nesne kabul edebilir veya bir türün bir özelliği bir temsilci tipinden olabilir.
Çoklu temsilciler
değiştirŞimdiye kadar bir temsilci nesnesi aynı anda yalnızca bir tane metodu temsil edebiliyordu. Artık bir temsilci nesnesinin aynı anda birden fazla metodu temsil edebilmesini sağlayacağız. Bu sayede temsilci nesnesi çağrıldığında temsil ettiği bütün metotlar sırasıyla çalıştırılacaktır. Bir temsilci nesnesinin birden fazla metodu temsil etmesini sağlamak için += ve -= operatörleri kullanılır. += operatörü temsilci nesnesine metot eklemek için, -= operatörü ise temsilci nesnesinden metot çıkarmak için kullanılır. Aslında aynı iş için + ve - operatörlerini de kullanabilirdik. Ancak += ve -= bu işi biraz olsun kolaylaştırıyor. Örnek:
using System;
class deneme
{
delegate void temsilci();
static void Metot1()
{
Console.WriteLine("Metot1 çağrıldı.");
}
static void Metot2()
{
Console.WriteLine("Metot2 çağrıldı.");
}
static void Metot3()
{
Console.WriteLine("Metot3 çağrıldı.");
}
static void Main()
{
temsilci nesne=null;
nesne+=new temsilci(Metot2);
nesne+=new temsilci(Metot1);
nesne+=new temsilci(Metot3);
nesne();
Console.WriteLine("***");
nesne-=new temsilci(Metot1);
nesne();
}
}
Bu programın ekran çıktısı şöyle olacaktır:
Metot2 çağrıldı. Metot1 çağrıldı. Metot3 çağrıldı. *** Metot2 çağrıldı. Metot3 çağrıldı.
Yani metotların çalışma sırası metotların temsilci nesnesine eklenme sırasıyla aynıdır. Eğer programda Metot1 temsilci nesnesine tekrar eklenirse bu sefer Metot1() en sona geçecektir.
NOT:
nesne+=new temsilci(Metot2);
nesne-=new temsilci(Metot1);
satırlarıyla
nesne=nesne+new temsilci(Metot2);
nesne=nesne-new temsilci(Metot1);
satırları denktir.
Statik olmayan metotların temsilcilerle kullanılması
değiştirTemsilciler statik metotlar yanında statik olmayan metotları da temsil edebilir. Bunun için temsilci nesnesini oluştururken parantez içine nesne.MetotIsmi
yazarız. Örnek:
using System;
class Sinif
{
delegate void Temsilci();
void Metot(){Console.WriteLine("Deneme");}
static void Main()
{
Sinif s=new Sinif();
Temsilci t=new Temsilci(s.Metot);
t();
}
}
Bu durumda temsilci nesnesi hem metodu, hem o metodun üzerinden çağrıldığı nesneyi temsil eder.
Delegate ve MulticastDelegate sınıfları
değiştirOluşturduğumuz bütün temsilci tanımları gizlice MulticastDelegate sınıfından türerler. Yani oluşturduğumuz temsilci nesneleriyle bu sınıfın üye elemanlarına erişebiliriz. Delegate sınıfı MulticastDelegate sınıfının anasıdır. Delegate ve MulticastDelegate sınıfları birer özet sınıf oldukları için
new
anahtar sözcüğüyle bu sınıflar türünden nesne yaratamayız. Yalnızca kendi yarattığımız temsilci nesneleri üzerinden bu sınıfların üye elemanlarına erişebiliriz. Şimdi isterseniz MulticastDelegate sınıfının bazı üye elemanlarını görelim:
- public override sealed Delegate[] GetInvocationList() Bu metot bir temsilcideki bütün metotları bir Delegate dizisine aktarır. İmzasından gördüğünüz üzere Delegate sınıfının metodunu override etmiştir, ancak kendisi yavru sınıfta override edilemez.
- public object DynamicInvoke(params object[] parametreler) Bu metot bir temsilcinin temsil ettiği metodu çağırmak için kullanılır. Delegate sınıfından devralınmıştır (override etmemiştir). Delegate nesneleri, MulticastDelegate nesnelerinin aksine direkt çalıştırılamaz. Bunun için DynamicInvoke() metodu kullanılır. Bu metodun tek görevi aslında DynamicInvokeImpl() metodunu çağırmaktır.
- protected virtual object DynamicInvokeImpl(object[] args) Bir Delegate veya MulticastDelegate nesnesini çalıştırmaya yarar. MulticastDelegate sınıfı, Delegate sınıfından devralmıştır. Sadece Delegate sınıfı veya bu sınıfı türeten sınıflar kullanabilir. Bildiğiniz gibi bütün temsilci tanımları MulticastDelegate sınıfından türemektedir. Bu durumda temsilci tanımının içinde bu metoda erişebiliriz, ama temsilci tanımlarının blokları olmaz. O halde biz bu metodu direkt çalıştıramayız. Biz az önce gösterdiğimiz DynamicInvoke() metodunu çalıştırırız, DynamicInvoke() metodu blokları içinde DynamicInvokeImpl() metodunu çağırır.
- public static Delegate Combine(params Delegate[] delegates) MulticastDelegate sınıfı Delegate sınıfından devralmıştır. Birden fazla Delegate nesnesini birleştirip tek bir Delegate nesnesi olarak tutmaya yarar. Sonuçta geriye bir Delegate nesnesi döndürüldüğü için bu nesnenin DynamicInvoke() metoduyla çalıştırılması gerekir, direkt çalıştırılamaz.
- public static Delegate Combine(Delegate a,Delegate b) MulticastDelegate sınıfı Delegate sınıfından devralmıştır. İki Delegate nesnesini birleştirip tek bir Delegate nesnesi olarak tutmaya yarar.
Bu metotların kullanımına bir örnek aşağıda verilmiştir.
using System;
class deneme
{
delegate void temsilci();
static void Metot1()
{
Console.WriteLine("Burası Metot1()");
}
static void Metot2()
{
Console.WriteLine("Burası Metot2()");
}
static void Main()
{
temsilci nesne=null;
nesne+=new temsilci(Metot1);
nesne+=new temsilci(Metot2);
Delegate[] dizi=nesne.GetInvocationList();
dizi[0].DynamicInvoke();
dizi[1].DynamicInvoke();
Delegate d=Delegate.Combine(dizi);
d.DynamicInvoke();
Delegate d2=Delegate.Combine(dizi[0],dizi[1]);
d2.DynamicInvoke();
}
}
Bu program aşağıdaki çıktıyı verir.
Burası Metot1() Burası Metot2() Burası Metot1() Burası Metot2() Burası Metot1() Burası Metot2()
Eğer Delegate dizisindeki temsilci nesnelerinin temsil ettiği metotların parametreleri varsa bu parametreler DynamicInvoke() metoduna object dizisi olarak verilmelidir.
NOT: Delegate sınıfı türünden bir nesnenin daima gizli türü olur, ve bu tür de daima bir temsilcidir. Yani Delegate sınıfı sadece bir kap vazifesi görür. Ayrıca Delegate sınıfı türünden bir nesneyle, bir MulticastDelegate nesnesinden farklı olarak normal metot çağrısı, += operatörüyle metot eklemesi ve -= operatörüyle metot çıkartımı yapılamaz. Delegate nesneleriyle bu işlemler yalnızca Delegate sınıfına ait olan üye elemanlarla yapılır. Ayrıca istersek tür dönüştürme operatörüyle Delegate nesnesini gizli türüne (ki bu bir temsilci oluyor) dönüştürebiliriz. Örnek:
using System;
class Ana
{
delegate void temsilci();
static void Metot1()
{
Console.WriteLine("Metot1");
}
static void Metot2()
{
Console.WriteLine("Metot2");
}
static void Metot3()
{
Console.WriteLine("Metot3");
}
static void Main()
{
temsilci t=new temsilci(Metot1);
t+=new temsilci(Metot2);
t+=new temsilci(Metot3);
Delegate d=t;
temsilci c=(temsilci)d;
c();
}
}
Bu program ekrana alt alta Metot1
, Metot2
ve Metot3
yazar.
NOT: Tür dönüştürme operatörüyle bir nesneyi daima gizli türüne dönüştürebiliriz. Bu işlem için tür içinde bir explicit metot tanımlamaya gerek yoktur.
İsimsiz metotlar
değiştirŞu ana kadar öğrendiklerimize göre temsilcilerin var olma nedeni metotlardı. Yani bir metot olmadan bir temsilci de olmuyordu. Ancak artık bir metot olmadan işe yarayan bir temsilci tasarlayacağız. Örnek:
using System;
class isimsiz
{
delegate double temsilci(double a,double b);
static void Main()
{
//temsilci nesnesine bir kod bloğu bağlanıyor.
temsilci t=delegate(double a,double b)
{
return a+b;
};
//temsilci nesnesi ile kodlar çalıştırılıyor.
double toplam=t(1d, 9d);
Console.WriteLine(toplam);
}
}
Gördüğünüz gibi bir temsilci nesnesine bir metot yerine direkt kodlar atandı.
Metot isimlerinin temsilci nesnelerine otomatik dönüşmesi
değiştirC# programlama dilinde metot isimleri (metot statik değilse nesne ve metot isimleri) temsilci nesnelerine bilinçsiz olarak dönüşebilir. Parametre olarak bir temsilci nesnesi alan metot, yapıcı metot, sahte özellik, indeksleyicinin ilgili parametresine direkt metot ismi yazılabilir. Örnek:
using System;
class Sinif
{
delegate void Temsilci();
void Metot() { Console.WriteLine("Deneme"); }
static void Main()
{
Sinif s = new Sinif();
Metot2(s.Metot);
}
static void Metot2(Temsilci t)
{
t();
}
}
Burada statik Metot2() metodu s nesnesi üzerinden çalıştırılan Metot() metodunu çalıştırmaktadır. Burada Metot2(s.Metot);
satırı yerine açık olarak Metot2(new Temsilci(s.Metot));
satırını da yazabilirdik. Zaten arkaplanda gerçekten çalıştırılan satır budur. C# bu şekildeki bir bilinçsiz dönüşümü olaylara temsilci nesnesi atarken, eklerken de yapmaktadır. Olayları bir sonraki derste göreceğiz.
Temsilcilerde kalıtım durumları
değiştirC# 2.0'dan önce temsilci nesnelerinin temsil edeceği metotların prototipi temsilcinin prototipi ile aynı olmalıydı. Yani gerek geri dönüş tipi, gerekse de parametrelerin tipi arasında bir türeme söz konusu olsa bile tür uyumsuzluğu var sayılıyordu. C# 2.0 ve daha sonraki sürümlerde programcılar bu gereksiz tür uyumu derdinden kurtulmuştur. Örnek program:
using System;
delegate Ana temsilciAna();
delegate Yavru temsilciYavru();
class Ana
{
}
class Yavru:Ana
{
}
class program
{
static Ana MetotAna()
{
return new Ana();
}
static Yavru MetotYavru()
{
return new Yavru();
}
static void Main()
{
temsilciAna nesneAna=new temsilciAna(MetotYavru);
//ancak aşağıdaki kod hatalı
//temsilciYavru nesneYavru=new temsilciYavru(MetotAna);
}
}
Gördüğünüz gibi geri dönüş tipi Ana olan temsilciAna() temsilcisi türünden nesne geri dönüş tipi Yavru olan MetotYavru metodunu temsil edebildi. Ancak geri dönüş tipi Yavru olan temsilciYavru() temsilcisi türünden nesne geri dönüş tipi Ana olan MetotAna metodunu temsil edemezdi.