Delegeler ve Olaylar (Delegates and Events) – I

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>Covariance(Ortak varyans) : Eşdeğişirlik olarak çevrilen bu durumda delegenin dönüş tipi ile delegenin temsil edeceği metodların dönüş tiplerinin uyumlu olması yani metodların geri dönüş tipi, delegenin geri dönüş tipinden türemiş olmalıdır. Örneğin Object türünde değer döndüren bir delegate, object türünden türemiş herhangi bir türü döndüren tüm metodları temsil edebilir.
< ı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.

Delegeler ve Olaylar (Delegates and Events) – I” hakkında 5 yorum

  1. Umyt

    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.

    Cevapla

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir