1. Giriş
Təsəvvür edin ki, sizin qəhvə maşınınız var. Adətən o sadəcə qəhvə bişirir və heç kəsi narahat etmir, amma bəzən şef deyir: «Zəhmət olmasa, qəhvə bişdikdən sonra “Hazır!” deyə qışqırarsan?» — və burada bir konfiqurasiya lazımdır. Qəhvə maşınını yenidən yazmırsınız. Sadəcə onun sonunda icra edəcəyi bir funksiyanı yaradırsınız.
C#-da bu rolu delegate-lər oynayır — onlar metodlara kod parçalarını (metodlar, lambda-lar və ya anonim metodlar) ötürməyə imkan verir ki, həmin metod onları lazım olan vaxt çağırsın. Sadə dillə desək, delegate müəyyən imzaya malik metodlara istinadları saxlaya bilən tipdir.
Delegate-in təyini
C#-da delegate delegate açar sözü ilə təyin olunur. Məsələn:
// Delegate, int qəbul edib bool qaytaran metodlara istinad edir
public delegate bool PredicateInt(int x);
İndi PredicateInt tipində dəyişən hər hansı bir metodu (və ya lambdanı!) istinad edə biləcək, hansı ki bir int qəbul edib bool qaytarır.
Delegate-lər nə üçün lazımdır?
- Loqikanı arqument kimi ötürmək (məsələn, sortlama, filtrləmə, event emalı üçün)
- Event-lərə abunə olmaq (bunlar barədə sonra danışacağıq)
- Callback-lərin həyata keçirilməsi
- Çağıran tərəf davranışın bir hissəsini müəyyən edən çevik API-lər
Sadə vizual sxem
| Delegate tipi | İmza | Çağırış nümunəsi |
|---|---|---|
|
|
|
|
|
|
|
|
|
2. Lambda necə delegate-ə çevrilir
Sintaksis
Məsələn, x => x > 5 kimi lambda yazanda, əslində delegate obyekti yaradırsınız. Lambda boşluqda «yaşamır»: ona mütləq bir tip lazımdır (kimsə onun parametrlər dəstini və qaytarılan tipini bilməlidir). Buna görə də C#-da lambda-ifadə həmişə kontekstdən asılı olaraq dolayısı ilə (və ya açıq şəkildə) delegate-ə çevrilir.
Nümunə 1: Delegate-in metoda bağlanması
// Explicit delegate təyini
public delegate bool MyPredicate(int number);
class Program
{
static void Main()
{
// Lambda-nı MyPredicate tipində dəyişənə təyin edirik
MyPredicate isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4)); // true
Console.WriteLine(isEven(7)); // false
}
}
Nümunə 2: Standart delegate-lərin istifadəsi
C# bir sıra ümumi delegate-lər təqdim edir: Action, Func<>, Predicate<>. Onlar demək olar ki, lambda yazdığınız hər yerdə istifadə olunur.
// Func
istifadə edirik Func
isPositive = number => number > 0; Console.WriteLine(isPositive(-5)); // false
3. Standart delegate-lər: Func, Action, Predicate
Func<...>
Nəsnə qəbul edib nəsə qaytaran metodlar üçün istifadə olunur.
İmza:
— Sonuncu tip qaytarılan dəyərdir, ondan əvvəlkilər parametr tipləridir, məsələn:
Func<int, string> — int qəbul edir, string qaytarır
Func
intToString = number => "Sayı: " + number; Console.WriteLine(intToString(7)); // "Sayı: 7"
Action<...>
Heç nə qaytarmayan (void) əməliyyatlar üçün istifadə olunur.
Action
printHello = name => Console.WriteLine("Salam, " + name + "!"); printHello("Vasiliy"); // "Salam, Vasiliy!"
Predicate<T>
Əslində Func<T, bool>-un qısaldılmış variantıdır. Obyekt üzərində məntiqi yoxlama tələb olunan zaman istifadə olunur (true/false).
Predicate
isOdd = x => x % 2 != 0; Console.WriteLine(isOdd(3)); // true
Vizual xatırlatma
| Delegate | İmza | Tətbiq |
|---|---|---|
|
|
Transformasiya, proyeksiya |
|
|
Yan-effektlər, çıxış |
|
|
Filtrləmə, axtarış |
Lambda-ifadəsi üçün hansı delegate tipi seçilməlidir?
- Əgər qaytarılan dəyər gözlənilirsə, Func<...> götürün
- Əgər metod heç nə qaytarmırsa (void), Action<...> istifadə edin
- Əgər məntiqi yoxlama lazımdırsa, Predicate<T> istifadə edin
Nümunə: Siyahını filtrləmə
List
numbers = new List
{ 1, 2, 3, 4, 5, 6 }; // Predicate
gözlənilir List
evenNumbers = numbers.FindAll(x => x % 2 == 0); Console.WriteLine(string.Join(", ", evenNumbers)); // 2, 4, 6
4. Faydalı nüanslar
Lambda-ifadələr və kolleksiya metodları: içində nə baş verir?
Kolleksiya metodunu çağıranda lambda ötürsəniz, məsələn:
var adults = users.Where(u => u.Age >= 18);
Where metodu arqument kimi Func<T, bool> tipini gözləyir. Yəni sizin u => u.Age >= 18 lambdanız kompilyator tərəfindən həmin tipdə delegate obyektinə çevrilir.
Blok-sxem: bu necə işləyir
Sizin lambda --> C# kompilyatoru --> Delegate obyekti (Func
) (u => u.Age >= 18) [tip məlumdur] (Where() içində çağırıla bilən)
Tiplə bağlı daha detallı: type inference
Adətən delegate tipi kontekstdən avtomatik çıxarılır. Məsələn, List<T>.Find metodu Predicate<T> gözlədiyi üçün kompilyator parametrin tipini metodun imzasından bilir.
List
words = new List
{ "one", "two", "three" }; var result = words.Find(word => word.Length == 5); // Find Predicate
gözləyir Console.WriteLine(result); // "three"
Əgər kontekst aydın deyilsə, kompilyatora kömək etmək lazım gəlir:
// Tipi açıq şəkildə göstəririk
Func
check = x => x > 10;
Delegate qaytaran metodlar: funksiyalar fabriki
Bəzən metodlar delegate qaytara bilər — yəni «funksiya fabrikləri» yaratmaq olar. Bu dinamik davranış yaratmaq üçün rahatdır.
// Delegate (lambda) qaytaran funksiya
Func
GetMultiplier(int factor) { return x => x * factor; } var times3 = GetMultiplier(3); Console.WriteLine(times3(5)); // 15
Bu ona görə işləyir ki, lambda (x => x * factor) xarici kontekstdən factor dəyişənini tutur (closure/bağlama) və Func<int, int> tipində obyekt kimi qaytarılır.
5. Delegate-lər və lambda-larla bağlı səhvlər və anlaşılmazlıqlar
İmzaların üst-üstə düşməməsi
Əgər parametrlər və ya qaytarılan dəyər uyğun gəlmirsə, kompilyator lambda-nı delegate-ə təyin etməyə imkan verməyəcək.
Func
f = x => "String qaytara bilməzsən!"; // Kompilyasiya səhvi
Lambda-nı delegate olmadan istifadə etməyə çalışmaq
Sadəcə lambda yazıb onu tipsiz çağırmaq olmaz:
// Bu işə yaramayacaq - kompilyator tipi çıxara bilməz
// var myFunc = x => x * 2; // CS0815 səhvi
// myFunc(10);
İşləməsi üçün tipi açıq göstərmək və ya kontekst vermək lazımdır:
Func
myFunc = x => x * 2; Console.WriteLine(myFunc(10)); // 20
Action, Func və Predicate arasında qarışıqlıq
Bəzən yanlış delegate tipi seçərək imza uyğunsuzluğu alınır. Sadə qaydanı yadda saxlayın: nəticə varsa — Func, heç nə qaytarılmırsa — Action (void), məntiqi cavab lazımdırsa — Predicate<T> (bool).
GO TO FULL VERSION