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…