Delegeler ve Olaylar (Delegates and Events) – I

C#, VB.NET, ASP.NET Add comments

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;
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.
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.

2 Responses to “Delegeler ve Olaylar (Delegates and Events) – I”

  1. Umyt Says:

    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.

  2. Sonay Says:

    Ciddi bir emek verilmiş olduğu çok net.. Teşekkürler.

Leave a Reply


9 × = 45

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS GiriÅŸ