C#’ta Delegate
C#’ta delegate (delege), fonksiyonları temsil etmek ve taşımak için kullanılan bir türdür. Delegate’ler, bir veya daha fazla fonksiyonu bir değişken gibi tutabilen ve başka bir fonksiyon tarafından çağrılabilen referans türleridir. Bir anlamda, delegate’ler fonsksiyonların birer “işaretçisi” veya “referansı” olarak düşünülebilir.
Delegate’ler, C#’ın olay tabanlı programlama ve işlevsel programlama özelliklerini desteklemek için kullanılır. Delegate’ler, fonksiyonları bir yerden başka bir yere geçirmek ve çağırmak için kullanılabilir. Bu sayede kod daha modüler ve esnek hale gelir. Delegate’ler, özellikle olaylar (events), geri çağırma (callback), ve asenkron işlemler gibi senaryolarda yaygın olarak kullanılır. Bu yüzden bu yazıda delegate’lere derinlemesine odaklanılarak aşağıdaki başlıklarda incelenecektir.
Başlıklar:
- Delegate Tanımlama
- Delegate’a Metot Set Etme
- Delegate Invoke (çağırma) Etme
- Delgate’i Callback Olarak Kullanma
- Multicast Delegate
- Generic Delegate
- Func Delegate
- Action Delegate
- Predicate Delegate
- Anonim Fonksiyonlar(Anonymous)
1) Delegate Tanımlama
Delegate tanımlarken “delegate” keyword’ü ile aşağıdaki imza gibi tanımlanır.
// İmza
[erişim belirleyici] delegate [geri dönüş tipi] [delegate adı]([parametreler]);
// Örnek
public delegate void IlkDelegate(string mesaj);
2) Delegate’e Metot Set Etme
Delegate tanımından sonra, delegate’e metot set edilebildiği gibi “lambda expression” ile de bir ifade set edilebilir. Fakat set edilen metotun imzası ile delegate’in imzası aynı olmalıdır.
// bir delegate tanımlanır
public delegate void IlkDelegate(string mesaj);
// hedef metoto set edilir. new keyword'ü ile delegate objesinden instance üretilir.
ILkDelegate del = new ILkDelegate(MetotA);
// veya
IlkDelegate del = MetotA;
// veya lambda expression ile set etme
IlkDelegate del = (string mesaj) => Console.WriteLine(mesaj);
// hedef metot
static void MetotA(string mesaj)
{
Console.WriteLine(mesaj);
}
3) Delegate Invoke Etme
Delegate’e metot set ettikten sonra, delegate “Invoke()” metodu ile veya “()” operatörü ile çağrılır.
del.Invoke("Hello World!");
// or
del("Hello World!");
4) Delegate’i Callback Olarak Kullanma
Delegate bir metota parametre olarak verilebilir. Daha sonra bu parametre ile verilen delegate invoke edilebilir. Bu şekilde verilen metot imzasına callback function denir.
public delegate void IlkDelegate(string mesaj); // delegate tanımı
static void Example(string[] args)
{
IlkDelegate ilk = ClassA.MetotA;
InvokeDelegate(ilk);
del = ClassB.MetotB;
InvokeDelegate(ilk);
ilk = (string msg) => Console.WriteLine("Lambda expression çağrıldı: " + msg);
InvokeDelegate(ilk);
}
static void InvokeDelegate(IlkDelegate ilk) // IlkDelegate parametresi
{
ilk("Hello World");
}
class ClassA
{
static void MetotA(string mesaj)
{
Console.WriteLine("ClassA.MetotA() parametresi ile : " + mesaj +" çağrıldı");
}
}
class ClassB
{
static void MetotB(string mesaj)
{
Console.WriteLine("ClassA.MetotB() parametresi ile : " + mesaj +" çağrıldı");
}
}
Yukarıdaki örnekte, “IlkDelegate” adında bir delegate tanımlanmıştır. Bu delegate, bir “string” parametre alıp consola mesaj yazan fonksiyonları temsil edebilir. Ardından, “MetotA” ve “MetotB” adında iki metot tanımlanmıştır ve bu metotlar “IlkDelegate” adındaki delegate’e atanmıştır. Son olarak, “ilk” değişkeni kullanılarak bu iki metotun referansları “InvokeDelegate” metotuna parametre olarak verilmişlerdir. “InvokeDelegate” metotu çağrıldığında bu ayrı sınıflarda bulunan “MetotA” ve “MetotB” işlerini yapacaktır.
5) Multicast Delegate
Bir delegate birden fazla metotu işaret edebilir. Eğer bir delegate birden fazla metotu işaret ediyorsa buna “Multicast” delegate denir. Bir delegate’e birden fazla metot set etmek için “+” veya “+=” operatörleri kullanılır. Aynı zamanda tetikleme zincirinden bir metot çıkarmak içinde “-” veya “-=” operaötü kullanılır.
static void Example2(string[] args)
{
IlkDelegate ilk1 = ClassA.MetotA;
IlkDelegate ilk2 = ClassB.MetotB;
IlkDelegate ilk = ilk1 + ilk2; // birleştirme ilk1 + ilk2
ilk("Hello World");
IlkDelegate ilk3 = (string mesaj) => Console.WriteLine("Lambda expression: " + mesaj);
ilk += ilk3; // birleştirme ilk1 + ilk2 + ilk3
ilk("Hello World");
ilk = ilk- ilk2; // ilk2 yi kaldırma
ilk("Hello World");
ilk -= ilk1; // ilk1 kaldırma
ilk("Hello World");
}
class ClassA
{
static void MetotA(string mesaj)
{
Console.WriteLine("ClassA.MetotA() parametresi ile : " + mesaj +" çağrıldı");
}
}
class ClassB
{
static void MetotB(string mesaj)
{
Console.WriteLine("ClassA.MetotB() parametresi ile : " + mesaj +" çağrıldı");
}
}
Eğer delegate bir değer döndürüyorsa, multicast delegate olarak çağırılıyorsa son metotun değeri geriye döner.
public delegate int IkinciDelegate(); // delegate tanımı
static void Main(string[] args)
{
IkinciDelegate del1 = ClassA.MetotC;
IkinciDelegate del2 = ClassB.MetotD;
IkinciDelegate del = del1 + del2;
Console.WriteLine(del());// returns 200 zincire eklenen son metotun değeri dönmüştür
}
class ClassA
{
static int MetotC()
{
return 100;
}
}
class ClassB
{
static int MetotD()
{
return 200;
}
}
6) Generic Delegate
Generic delegate’ler aynı şekilde “delegate” keyword’ü ile generic “T” ile tanımlanırlar. Faket delegate’e geçirilecek tip, metot set etme aşamasında belirtilir.
public delegate T islem<T>(T param1, T param2); // generic delegate
class GenericDelegate
{
static void Demo()
{
islem<int> top = Topla;
Console.WriteLine(top(10, 20));
islem<string> bir = Birlesir;
Console.WriteLine(bir("Hello ", "World!!"));
}
public static int Topla(int say1, int say2)
{
return say1 + say2;
}
public static string Birlesir(string str1, string str2)
{
return str1 + str2;
}
}
Yukarıda generic iki parametre alıp generic bir deger döndüren “islem” adında bir delegate tanımlanmıştır. Bu generic delegate’e aynı imzaya sahip farklı metot set edilip farklı işlemler yapılmıştır.
7) Func Delegate
.NET’te Func ve Action tipleri built-in generic delegate tipleridir. Bu yüzden çoğu olayda delegate kullanmak için .NET kendi içerisinde bu iki generic delegate’i barındırır. Func delegate’i “System” namespace’inde bulunur. Kendisi 0 ve ya daha fazla parametre alırken return olarak sadece bir tane parametre döner. Tanımı aşağıdaki gibidir.
namespace System
{
public delegate TResult Func<in T, out TResult>(T arg);
}
<> parantezleri arasında bulunan “in T” ile “out TResult” Func delegate’inin giriş çıkış parametrelerini temsil ederler. Eğer Func delegate’i iki parametre alıyorsa aşağıdaki gibi tanımlanır.
namespace System
{
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
}
// Kısa tanımı
Func<int, int, int> sum; // sonuncu parametre out parametresidir.
Func delegate 0 ile 16 arasında farklı tiplerde parametre alabilmektedir. Bu parametrelerde sonuncu olanı out return parametresidir. Aşağıdaki Func delegate’inin input parametresi yoktur sadece bir tane out parametresi vardır.
Func<int> getRandomNumber;
Örnek:
class Program
{
static int Topla(int a, int b)
{
return a + b;
}
static void Demo()
{
Func<int,int, int> top = Topla;
int sonuc = top(10, 10);
Console.WriteLine(sonuc); // 20
}
}
a) Func Delegate ve Anonim Metotlar: Func delegate’ini anonim bir metot ile kullanılabilir. Bunun için, “delegate” keyword’ü ile bir anonim metot oluşturularak set edilmelidir.
Func<int> sayiGetir = delegate() // delegate keyword
{
Random rnd = new Random();
return rnd.Next(50, 150);
};
b) Func Delegate ve Lambda Expression: Func delegate’i bir lambda expression ile kullanılabilir.
Func<int> sayiGetir = () => new Random().Next(1, 100);
// veya
Func<int, int, int> Topla = (x, y) => x + y;
c) Func Delegate Özellikleri
- Func delegate .NET içinde buil-in olarak gelir.
- Func delegate bir değer döndürmek zorundadır.
- Func delegate 0 ile 16 arasında parametre alabilir.
- Func delegate “ref” ve “out” parametrelerini desteklemez.
- Func delegate anonim metot ve lanbda expression ile kullanılabilir.
8) Action Delegate
Action delegate “System” namespace’inde tanımlıdır. Action delegate Func delegate ile aşağı yukarı aynıdır. Sadece Func delegate bir değer return etmek zorundayken Action delegate bir değer return etmek zorunda değildir. Yani geri dönüş tipi void olan bir metot imzasıyla kullanılabilir. Ayrıca, Action delegate de 0 ile 16 arasında farklı tiplerde parametre alabilmektedir.
public delegate void Yazdir(string mesaj); // delegate tanımı
static void ConsoleYazdir(string mesaj)
{
Console.WriteLine(mesaj);
}
static void Main(string[] args)
{
Yazdir yz = ConsoleYazdir;
yz("Merhaba");
}
// Çıktı
// Merhaba
// Burada "Yazdir" isimli delegate tanımı yapılmadan "Action" delegate kullanılarak aynı işlem yaptırılabilir.
static void Main(string[] args)
{
Action<string> yz = ConsoleYazdir;
yz("Merhaba");
}
// Çıktı
// Merhaba
a) Action Delegate ve Anonim Metotlar: Action delegate’ini anonim bir metot ile kullanılabilir. Bunun için, “delegate” keyword’ü ile bir anonim metot oluşturularak set edilmelidir.
static void Main(string[] args)
{
Action<int> printActionDel = delegate(int i) { Console.WriteLine(i); };
printActionDel(10);
}
b) Action Delegate ve Lambda Expression: Action delegate’i bir lambda expression ile kullanılabilir.
static void Main(string[] args)
{
Action<int> printActionDel = i => Console.WriteLine(i);
printActionDel(10);
}
c) Action Delegate Özellikleri
- Action delegate .NET içinde buil-in olarak gelir.
- Action delegate bir değer döndürmek zorunda değildir.
- Action delegate 0 ile 16 arasında parametre alabilir.
- Action delegate anonim metot ve lambda expression ile kullanılabilir.
8) Predicate Delegate
Predicate, Func ve Action gibi bir delegate’tir. Göndrilen parametrelere göre içinde bir kriter barındıran bir metotu temsil eder. Yani gönderilen parametrelerin koşulu karşılayıp karşılamama durumuna göre davranış sergileyen metodları temsil eder. Predicate delegate’i bir tane input parametre alıp kritere göre true veya false döner. Predicate delegate “System” namespace’inde tanımlıdır. Predicate imzası aşağıdaki gibidir.
public delegate bool Predicate<in T>(T obj);
static bool IsUpperCase(string str)
{
return str.Equals(str.ToUpper());
}
static void Main(string[] args)
{
Predicate<string> isUpper = IsUpperCase;
bool result = isUpper("hello world!!");
Console.WriteLine(result);
}
// Çıktı
// False
Diğer delegate’ler gibi anonim metotolar ve lambda expression ile de kullanılabilir.
a) Predicate Delegate ve Anonim Metotlar: Predicate delegate’ini anonim bir metot ile kullanılabilir. Bunun için, “delegate” keyword’ü ile bir anonim metot oluşturularak set edilmelidir.
static void Main(string[] args)
{
Predicate<string> isUpper = delegate(string s) { return s.Equals(s.ToUpper());};
bool result = isUpper("hello world!!");
}
b) Predicate Delegate ve Lambda Expression: Predicate delegate’i bir lambda expression ile kullanılabilir.
static void Main(string[] args)
{
Predicate<string> isUpper = s => s.Equals(s.ToUpper());
bool result = isUpper("hello world!!");
}
c) Predicate Delegate Özellikleri
- Predicate delegate .NET içinde buil-in olarak gelir.
- Predicate delegate bir parametre alıp boolean değer döndürür.
- Predicate delegate anonim metot ve lambda expression ile kullanılabilir.
10) Anonim Fonksiyonlar
Anonim metotlar adında anlaşılacağı üzere adı olmayan metotlardır. Anonim metotlar “delegate” keyword’ü ile tanımlanıp bir delegate’e set edilen metotlardır.
public delegate void Yazdir(string mesaj);
static void Main(string[] args)
{
Print print = delegate(string mesaj) {
Console.WriteLine("Anonim metot içindeki değer. Value: {0}", mesaj);
};
print("Merhaba");
}
// Çıktı
// Anonim metot içindeki değer. Value: Merhaba
a) Lokal Değişkenlere Erişim: Anonim metot bulunduğu metotun içindeki değişkenlere erişebilir.
public delegate void Yazdir(string mesaj);
static void Main(string[] args)
{
string selam = "Selam.";
Print print = delegate(string mesaj) {
Console.WriteLine("Anonim metot içindeki değer. Value: {0}", selam + mesaj); // selam local değişkenine erişiyor.
};
print("Merhaba");
}
// Çıktı
// Anonim metot içindeki değer. Value: Selam Merhaba
b) Anonim Metotun Parametre Olması: Anonim metot, delegate’i parametre olarak kabul eden bir metota parametre olarak gönderilebilir.
public delegate void Yazdir(string mesaj); // delegate tanımlama
class Program
{
public static void YazdirMetot(Yazdir yzDel, string mesaj) // delegate parametresi alan metot
{
mesaj+= " Dünya!";
yzDel(mesaj);
}
static void Main(string[] args)
{
YazdirMetot(delegate(string mesaj) { Console.WriteLine("Anonymous method: {0}", mesaj); },"Merhaba");// anonim metot parametre olarak gönderiliyor
}
}
// Çıktı
// Anonymous method: Merhaba Dünya!
c) Event Handler: Anonim metot event handler olarak ta kullanılabilir.
saveButton.Click += delegate(Object o, EventArgs e) // delegate kelimesi ile anonim metoto oluşturuluyor.
{
System.Windows.Forms.MessageBox.Show("Save Successfully!");
};
d) Anonim Metotların Özellikleri:
- Anonim metotlar delegate keyword’ü ile tanımlanır.
- Anonim metotlarda lambda expression desteği C# 3.0 ile geldi.
- Anonim metot bir delegate’e atanmalıdır.
- Anonoim metotolar içinde bulundukları metotun değişkenlerine erişebilirler.
- Anonim metotlar parametre olarak gönderilebilirler.
- Anonim metotlar event handler olarak ta kullanılabilir.
Delegate’ler, C# dilinde güçlü bir araç olarak kullanılır ve kodun daha esnek, yeniden kullanılabilir ve modüler olmasını sağlar. Delegeler, fonksiyonel programlamanın birçok avantajını sağlayarak, daha karmaşık senaryolarda fonksiyonları kolayca taşıyabilir ve çağırabiliriz.
Yararlanılan kaynaklar:
Microsoft CLR via C# Fourth Edition kitabı.