Değişkenler gibi metodlar da bellek üzerinde özel bir alanda yaşar. Değişken pointerleri gibi metodların başlangıç adresini işaret eden pointerler bulunur. Bunlara metod işaretçileri (function pointers) denilir. İstenirse metodlar, hafıza üzerinden sadece isimleriyle değil bu pointerler sayesinde de çağrılabilir. C veya C++’da bu yöntem yoğun olarak kullanılmaktaydı. .NET’in eskiden alışagelmiş unmanaged pointer desteği C# ile sınırlıdır. VB.NET, bu destekten yoksundur. Fakat desteklediği pointer yapısı CLS ile uyumlu olmadığı için tür güvenliği sunmaz. Bu yüzden .NET, fonksiyon işaretçileriyle aynı amaca sahip tür güvenliği sağlayan delege (delegate) isimli işaretçiler sunar.
Fransızca’daki “délégué” sözcüğünden dilimize geçmiş olan delege, terim olarak, kendisine yetki verilerek bir yere veya birinin katına gönderilen kimse, elçi anlamına gelir. Yazılım dilinde ise metodlara aracılık eden, onları temsil eden, onlara erişmemizi sağlayan çalışma zamanında tanımlanan yapılardır. Delegate, daha önceki function pointerler’den farklı olarak sadece statik metodları işaret etmez, hem statik hem de nesne (instance) metodları işaret eder.
Peki delegatelere nerede ihtiyaç duyarız. İki yerde ihtiyaç duyarız; Bilindiği gibi uygulamamızın hangi metodları yürüteceği derlenme aşamasında belirlenir. Böylece CLR, çalışma zamanında, zamanı geldiğinde ilgili metodları çalıştırır. Fakat bazen çalışma zamanında planda olmayan bir metodu yürütmek zorunda kalabiliriz. Bunun için delegateler kullanılır. Delegateleri kullandığımız ikinci alan ise event’lerdir. Bir olay(event) gerçekleştiği zaman bir veya daha fazla metodu, gerçekleşen olaydan haberdar etmek için delegeler kullanırız. Olay gerçekleştiği zaman hangi metodlar çağrılacaksa onları delegelere eklemeliyiz. Aslında delegeler, eventlerle anlam kazanır. Bir sonraki bölümde bunu anlatacağız.
Delegateleri kullanma, üç aşamada gerçekleştirilir;
1. Tanımlama(Declaration),
2. Örnekleme, gerçeklenme (Instantiation),
3. Çağırma(Invocation)
Delegeler, VB.NET ve C# tarafında “delegate” sözcüğüyle, metodlar gibi tanımlanır. Function veya Sub method olabilirler. Delegeleri, class’lar gibi tanımlayabiliriz nitekim derleyici, MSIL tarafında oluşturduğumuz delege ile aynı isimde ve System.MulticastDelegate sınıfından inherit edilmiş bir class yaratır. Aşağıda iki tane delege tanımlanmıştır.
Delegeler, VB.NET ve C# tarafında “delegate” sözcüğüyle, metodlar gibi tanımlanır. Function veya Sub method olabilirler. Delegeleri, class’lar gibi tanımlayabiliriz nitekim derleyici, MSIL tarafında oluşturduğumuz delege ile aynı isimde ve System.MulticastDelegate sınıfından inherit edilmiş bir class yaratır. Aşağıda iki tane delege tanımlanmıştır.
Public Delegate Sub MyDelegate1() Public Delegate Function MyDelegate2(ByVal Deger As Integer) As Integer
public delegate void MyDelegate1(); public delegate int MyDelegate2(int Deger);
Görüldüğü gibi aslında delegateler, başka metodlara aracılık eden özel metodlardır. Bir delegeyi kullanılabilir yapmak için ondan bir instance yaratıp parametre olarak hangi metodları temsil edeceği girilmelidir. Burada dikkat edilmesi gereken nokta, delegate’i temsilci kabul edecek metod veya metodların delegate ile aynı signature yapısına sahip olması gerekir yani parametre sayı, sıra ve türlerinin aynı olması gerekir. Örnekteki MyDelegate1 delegesi, parametre almayan ve geriye bir değer döndürmeyen tüm metodları temsil edebilir. Aynı şekilde MyDelegate2 delegesi, sadece dışardan integer türünde parametre alan ve geriye integer türü değer döndüren metodları temsil edebilir. Metodları, delegeyle ilişkilendirmek için VB.Net tarafında metodların hafıza üzerindeki adresini döndüren AddressOf operatörü, C# tarafında ise metodun ismi kullanılır. Çünkü C#’ta metodun ismi aynı zamanda metodun adresini açıklar.
Delegate’lerin sağladığı diğer özellik ise metodlara parametre olarak başka bir metodu göndermemize izin vermesidir.
Ayrıca delegate özel bir sınıf türü olduğu için, namespace elemanı olarak class dışında da tanımlanabilir.
Aşağıdaki gibi MyDelagate isimli delegeyi “Islem” metodunu temsil edecek şekilde kullanıma hazırlayabiliriz.
Module Module1
'Declaration
Public Delegate Function MyDelegate(ByVal Deger As Integer) As Integer
Function KareAl(ByVal Sayi As Integer) As Integer
Return Sayi * Sayi
End Function
Sub Main()
Dim Delege As New MyDelegate(AddressOf KareAl) 'Instantiation
Console.ReadLine()
End Sub
End Module
class Program
{
public Program ()
{//Constructor
MyDelegate delege = new MyDelegate(KareAl);
//Instantiation
}
//Declaration
public delegate int MyDelegate(int Deger);
int KareAl(int Sayi)
{
return Sayi * Sayi;
}
static void Main()
{
new Program();//Tüm üyeleri static yapmayalım diye.
Console.ReadLine();
}
}//Program
class Program
{
//Declaration
public delegate int MyDelegate(int Deger);
static int KareAl(int Sayi)
{
return Sayi * Sayi;
}
static void Main()
{
MyDelegate delege = new MyDelegate(KareAl);//Instantiation
Console.ReadLine();
}//Main
}//Program
Böylece MyDelegate isimli delegeye KareAl() metodunu çağırma yetkisi verildi. Delege üzerinde fonksiyonu çağırmak için instance’yi kullanmak yeterli olacaktır.
'........
Sub Main()
Dim Delege As New MyDelegate(AddressOf KareAl) //Declaration
Dim Kare As Integer = Delege(5) //Invocation
Console.WriteLine("5'in karesi :{0}", Kare)
End Sub
'........
//......
public Program(){
MyDelegate delege = new MyDelegate(KareAl); //Declaration
int Kare = delege(5); //Invocation
Console.WriteLine("5'in karesi :{0}",Kare);
}
//......
5'in karesi :25
C# 2.0 ile birlikte gelen bir özellik olarak delegate’in bir metodla ilişkilendirilmesi aşağıdaki gibi daha kısa bir ifadeyle yapılabilir.
MyDelegate delege = new MyDelegate(KareAl);//Eski Format MyDelegate delege = KareAl;//Yeni Format
Delege üzerinden metodları çağırdığımızda arka tarafta delegate’in Invoke() metodunu çağırmış oluyoruz. Yani “delege(5)” ifadesi, “delege.Invoke(5)” metodunu çalıştırır.
Konun başında bir delegate’in, bir metodu temsil edebilmesi için her ikisinin signature yapılarının aynı olması gerektiğini söylemiştik. C# 2.0 ile birlikte bu zorunluluk, metod ile delegenin parametre veya dönüş tipleri arasında kalıtıma dayalı bir ilişkinin olması olarak değiştirildi. Burada iki durum sözkonusudur;
< ıl>Contravariance(Karşıt varyans) : Karşıtdeğişirlik olarak çevrilen bu durumda delegenin aldığı parametre tipi ile delegenin temsil edeceği metodların parametre tiplerinin uyumlu olmasıdır. Burada karşıt bir durum sözkonusu yani delegenin parametre tipi, temsil edeceği metodların parametre tipinden türemiş olması gerekir. Örneğin, String tipinde parametre alan bir delegate, Object gibi String tipinin türediği tiplerde parametre alan metodları temsil edebilir.
Bu iki durumunu, sırasıyla örneklendirelim;
[Covariance Durumu]
class Program
{
delegate object Temsilci();
static object Metod1()
{
return "";
}//Metod1
static string Metod2()
{
return "";
}//Metod2
static Array Metod3()
{
return null;
}//Metod3
static void Main()
{
/*========ESKİ FORMAT===========
Temsilci Delege = new Temsilci(Metod1);
Delege = new Temsilci(Metod2);//C# 1.1'de HATALI DURUM
Delege = new Temsilci(Metod3);//C# 1.1'de HATALI DURUM
*/
//========YENİ FORMAT===========
Temsilci Delege = Metod1;
Delege = Metod2;//C# 1.1'de HATALI DURUM
Delege = Metod3;//C# 1.1'de HATALI DURUM
}//Main
}//Program
Bu örnekte, delegate’in dönüş tipi, Object türündedir. Metod2()’nin dönüş tipi ise String türündedir. String türü, Object türünden türediği için bu delegate, Metod2() metodunu temsil edebilir. Aynı şekilde, Array tipi Object tipinden türediği için bu delegate, Metod3() metodunu da temsil edebilir.
[Contravariance Durumu]
using System;
using System.Data.Common;
using System.Data.SqlClient;
class Program
{
delegate void Temsilci(SqlConnection Obj);
static void Metod1(SqlConnection Obj)
{
Obj = null;
}//Metod1
static void Metod2(DbConnection Obj)
{
Obj = null;
}//Metod2
static void Metod3(object Obj)
{
Obj = null;
}//Metod3
static void Main()
{
Temsilci Delege = Metod1;
Delege = Metod2;//C# 1.1'de HATALI DURUM
Delege = Metod3;//C# 1.1'de HATALI DURUM
}//Main
}//Program
Bu örnekte, delegate’in aldığı parametre tipi, SqlConnection türündedir. Metod2()’nin parametre tipi ise DbConnection türündedir. SqlConnection türü, DbConnection türünden türediği için bu delegate, Metod2() metodunu temsil edebilir. Aynı şekilde, SqlConnection tipi Object tipinden türediği için bu delegate, Metod3() metodunu da temsil edebilir.
Delegate’lerdeki bu covariance ve contravariance durumlarını, kalıtımsal olarak birbirleriyle ilişkili iki class üzerinde göstermemiz, konunun daha net anlaşılmasında faydalı olacağını düşünüyorum.
[Covariance Durumu]
public class Canli
{
}
//Canli'dan türemiş Insan sınıfı
public class Insan : Canli
{
}
//Delegate, Canli türünde değer döndürmektedir
public delegate Canli Temsilci();
class Program
{
public static Canli Metod1()
{
return null;
}//Metod1
public static Insan Metod2()
{
return null;
}//Metod2
static void Main()
{
Temsilci Delege = Metod1;
Delege = Metod2; //C# 1.1'de HATALI DURUM
}//Main
}//Program
[Contravariance Durumu]
public class Canli
{
}
//Canli'dan türemiş Insan sınıfı
public class Insan : Canli
{
}
//Delegate, Canli türünde değer döndürmektedir
public delegate void Temsilci(Insan Obj);
class Program
{
public static void Metod1(Insan Obj)
{
Obj = null;
}//Metod1
public static void Metod2(Canli Obj)
{
Obj = null;
}//Metod2
static void Main()
{
Temsilci Delege = Metod1;
Delege= Metod2; //C# 1.1'de HATALI DURUM
}//Main
}//Program
Bir delegeyi C veya C++’taki function pointerlerden farklı olarak birden fazla metoda temsilci kılabiliriz. Buna çoklu temsilcilik (Multicasting) denilir. Delegenin listesine yeni bir metod eklemek için C#’ta “+=” operatörü, VB.Net’te Combine() metodu kullanılır. Listeden bir metod çıkarmak için C#’ta “-=” operatörü, VB.Net’te Remove() metodu kullanılır. Bir delegeyle ilişkilendirilmiş metodlar o delegenin çağrı listesinde yer alır. Bu yüzden delegeyle ilişkilendirilmiş metodlara erişmek için delegenin InvocationList() metodu kullanılır.
NOT : VB.Net’teki Combine() metodunun iki tane overload yapısı var. İlki, sadece iki tane delegate alır.
Public Shared Function Combine(ByVal a As System.Delegate, ByVal b As System.Delegate) As System.Delegate
Eğer ikiden fazla delegate’i combine edeceksek o zaman Combine’nin dizi alan yapısını kullanacağız.
Public Shared Function Combine(ByVal delegates() As System.Delegate) As System.Delegate
Aşağıdaki uygulamada MyDelegate isimli bir delege tanımlanmış ve aynı anda 3 metoda aracılık etmesi sağlanmıştır. Böylece birden fazla metodu bir bütün olarak çalıştırmış olacağız.
Module Module1
Public Delegate Sub MyDelegate(ByVal Deger1 As Integer, ByVal Deger2 As Integer)
Sub Toplama(ByVal Sayi1 As Integer, ByVal Sayi2 As Integer)
Console.WriteLine("{0} + {1} = {2}", Sayi1, Sayi2, Sayi1 + Sayi2)
End Sub
Sub Carpma(ByVal Sayi1 As Integer, ByVal Sayi2 As Integer)
Console.WriteLine("{0} * {1} = {2}", Sayi1, Sayi2, Sayi1 * Sayi2)
End Sub
Sub Bolme(ByVal Sayi1 As Integer, ByVal Sayi2 As Integer)
Console.WriteLine("{0} / {1} = {2}", Sayi1, Sayi2, Sayi1 / Sayi2)
End Sub
Sub Main()
Dim Delege1 As MyDelegate = New MyDelegate(AddressOf Toplama)
Dim Delege2 As MyDelegate = New MyDelegate(AddressOf Carpma)
Dim Delege3 As MyDelegate = New MyDelegate(AddressOf Bolme)
Dim CombDeleges() As MyDelegate = {Delege1, Delege2, Delege3}
'Veya metodları direk bu satırda da tanımlayabilirdik.
'Dim CombDeleges() As MyDelegate = {AddressOf Toplama, AddressOf Carpma, AddressOf Bolme}
Dim delege As MyDelegate
delege = [Delegate].Combine(CombDeleges)
'Veya
'delege = delege.Combine(CombDeleges)
Dim X, Y As Integer
X = 18 : Y = 3
delege(X, Y)
Console.ReadLine()
End Sub
End Module
class Program
{
public Program()
{//Constructor
MyDelegate delege = null ;
delege += new MyDelegate(Toplama);
delege += new MyDelegate(Carpma);
delege += new MyDelegate(Bolme);
int X,Y; X= 18; Y=3;
delege(X,Y);
}//Program
public delegate void MyDelegate(int Deger1,int Deger2);
void Toplama(int Sayi1,int Sayi2)
{
Console.WriteLine("{0} + {1} = {2}",Sayi1,Sayi2,Sayi1 + Sayi2);
}//Toplama
void Carpma(int Sayi1,int Sayi2)
{
Console.WriteLine("{0} * {1} = {2}",Sayi1,Sayi2,Sayi1 * Sayi2);
}//Carpma
void Bolme(int Sayi1,int Sayi2)
{
Console.WriteLine("{0} / {1} = {2}",Sayi1,Sayi2,Sayi1 / Sayi2);
}//Bolme
static void Main()
{
new Program();//Tüm üyeleri static yapmayalım diye.
Console.ReadLine();
}//Main
}//Program
18 + 3 = 21
18 * 3 = 54
18 / 3 = 6
Şimdi de delegenin temsil ettiği listeden bir metod çıkaralım (Remove() metodu). Ve temsil ettiği güncel listeyi çekelim (GetInvocationList() fonksiyonu).
'Toplama metodu kaldırılıyor.
delege = [Delegate].Remove(delege, Delege1)
'veya
'delege = delege.Remove(delege, New MyDelegate(AddressOf Toplama))
Dim DlgList() As [Delegate] = delege.GetInvocationList()
Console.WriteLine("Delegenin temsil ettiği metodlar :")
Console.WriteLine("-----------------------------------")
For i As Byte = 0 To DlgList.Length - 1
Console.WriteLine(DlgList(i).Method.Name)
Next
//Toplama metodu kaldırılıyor.
delege -= new MyDelegate(Toplama);
Delegate[] DlgList =delege.GetInvocationList();
Console.WriteLine(“Delegenin temsil ettiği metodlar :”);
Console.WriteLine(“———————————–“);
for(byte i=0;i
Delegenin temsil ettiği metodlar :
-----------------------------------
Carpma
Bolme
Delegate’in kullanım biçim ve amacına baktığımızda delegate’leri kullanarak metodları encapsulate ettiğimizi söyleyebiliriz. Sonraki yazıda delegateleri yoğun kullandığımız event konusuna değineceğiz.
Ahmet bey çok teşekkürler. 3 yıldır yazılım öğrenmeye çalışıyorum ilk defa delegenin ne demek olduğunu anlamış bulunuyorum. Saygılar.
Ciddi bir emek verilmiş olduğu çok net.. Teşekkürler.
Başından sonuna kadar atlanmadan okunması gereken bir yazı olmuş. Emeğinize sağlık.
Çok güzel bir yazı olmuş emeğinize sağlık. Teşekkürler
Harika bir sunum. Teşekkürler…