CodeGym /Kurslar /C# SELF /Lambda-ifadələrinin və delegate-lərin birləşdirilməsi

Lambda-ifadələrinin və delegate-lərin birləşdirilməsi

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Delegate — funksiyanın “imzasını” təsvir edən tipdir: hansı parametr(lər)i qəbul edir, nə qaytarır. Bu funksiyanın pasportu və ya kod üçün gecə klubuna keçiddir: imza uyğundursa — keç, uyğun deyilsə — üzr istəyirik, buraxmırlar.

Nümunə:

public delegate int Operation(int a, int b);

Bu cür delegate iki int qəbul edib int qaytaran funksiyanı bildirir. Belə proqramlaşdırma "lamblardan əvvəl" belə idi (gözəl, amma çox rahat deyil):

Operation add = delegate (int x, int y)
{
    return x + y;
};

Lambda-lar gələn kimi kod daha yığcam oldu:

Operation add = (x, y) => x + y;

Maraqlı fakt: C#-da lambda-ifadə sadəcə “anonymous function” yaratmaq üsuludur, hansı ki uyğun tipdə delegate nümunəsinə avtomatik çevrilir. Beləliklə onların əlaqəsi demək olar ki, fərqlənməz olur!

Daxili delegate-lər: Func, Action, Predicate

Hər vəziyyət üçün öz delegate-lərini yazmamaq üçün C#-da universal tip-funksiyalar var:

  • Func<T1, T2, ..., TResult> — parametrlər (T1, T2, ...) qəbul edib TResult tipində dəyər qaytarır.
  • Action<T1, T2, ...> — demək olar eynidir, amma heç nə qaytarmır (void).
  • Predicate<T> — bir parametr qəbul edib bool qaytaran universal delegate (filter-lərin sevimlisi).
Delegate Qaytarılan tip Nümunə sintaksisi
Func<int, int>
int
Func<int, int> f = x => x + 1;
Action<string>
void
Action<string> a = s => ...;
Predicate<double>
bool
Predicate<double> p = d => ...;

Nümunə:

Func<int, int, int> add = (a, b) => a + b;
Action<string> show = s => Console.WriteLine(s);
Predicate<int> isEven = n => n % 2 == 0;

2. Lambda-ifadələri delegate “zərfi” içində

Lambda-nın delegate tipli metoda ötürülməsi

Hər hansı bir metod delegate qəbul edirsə, onun gözlədiyi imzaya uyğun olan lambda da qəbul edilə bilər.

public static void CalculateAndShow(int a, int b, Func<int, int, int> operation)
{
    int result = operation(a, b);
    Console.WriteLine($"Nəticə: {result}");
}

// Lambda ilə çağırırıq
CalculateAndShow(5, 7, (x, y) => x * y); // Nəticə: 35

Action nümunəsi

void ProcessString(string message, Action<string> processor)
{
    processor(message);
}

// Lambda istifadə edirik
ProcessString("Salam, dünya!", s => Console.WriteLine(s.ToUpper()));

Predicate nümunəsi

List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = numbers.FindAll(n => n % 2 == 0);

Belə anlarda başa düşürsən: niyə öz delegate-ləri ixtira edəsən ki, Microsoft-un sınanmış "təkəri" var.

Delegate-lər və lambda-lar birlikdə necə işləyir

flowchart TD
    A[Lambda-ifadə] -->|Yaratır| B((Delegate))
    B -->|Argument kimi ötürülür| C[Metod]
    B -->|Saxlanılır| D[Xüsusiyyət/Sahə/Kolleksiya]
    C -->|Çağırır| E[Lambda kodu]

3. Lambda-nın öz metodlarımızda delegate kimi ötürülməsi

Əgər öz metodunuz delegate-lərdən istifadə edirsə, lambda onun istifadəsini maksimum dərəcədə elastik edir.

Nümunə: bizim mini-kalkulyator

// Əməliyyatlarla işləyən metod müəyyən edirik
static int PerformOperation(int a, int b, Func<int, int, int> operation)
{
    return operation(a, b);
}

// Fərqli əməliyyatları tətbiq edirik (lambda-lar işdə!)
int sum = PerformOperation(3, 4, (x, y) => x + y);
int multiply = PerformOperation(3, 4, (x, y) => x * y);
int max = PerformOperation(3, 4, (x, y) => x > y ? x : y);

Console.WriteLine(sum);      // 7
Console.WriteLine(multiply); // 12
Console.WriteLine(max);      // 4

Baxırsan, loqika necə elegnt dəyişir, üç ayrı metoda ehtiyac yoxdur.

4. Lambda-lar + delegate-lər LINQ-da (və başqa yerlərdə)

LINQ-da metodlar “kiçik məntiq funksiyası” gözləyir, yəni delegate. Əksər standart LINQ metodları belə tiplər qəbul edir:

  • Func<T, bool> filter üçün (Where)
  • Func<T, TResult> transformasiya üçün (Select)
  • Func<T, TKey> sıralama üçün (OrderBy)
List<string> names = new List<string> { "Anna", "Oleg", "Viktor", "Yana" };

// 3-dən uzun olan adları alırıq
var longNames = names.Where(name => name.Length > 3);

// Adları böyük hərflərə çeviririk
var upperNames = names.Select(name => name.ToUpper());

LINQ — "lambda-ların maksimuma çəkilməsi" kimidir, çünki delegate kimi ötürülə bilən hər şey lambda ilə ötürülə bilər.

5. Birləşdirmə: lambda-delegate-lərin saxlanması və ötürülməsi

Funksiyalar siyahısı

Lambda sadəcə müvəqqəti anonymous metod deyil. Onları kolleksiyada saxlayıb dinamik istifadə edə bilərsən.

List<Func<int, int, int>> operations = new List<Func<int, int, int>>
{
    (x, y) => x + y,
    (x, y) => x - y,
    (x, y) => x * y,
    (x, y) => x / y
};

foreach (var op in operations)
{
    Console.WriteLine(op(10, 2));
}

Belə yanaşma testlərdə, handler-lərdə və plugin-lərdə tez-tez rast gəlinir.

Delegate-lərin dictionary-si

Əgər sən plug-and-play arxitekturasının fanatısansa:

var mathFuncs = new Dictionary<string, Func<int, int, int>>
{
    { "plus", (x, y) => x + y },
    { "minus", (x, y) => x - y },
    { "pow", (x, y) => (int)Math.Pow(x, y) }
};

string command = "pow"; // İstifadəçi girişinin təqlidi

if (mathFuncs.TryGetValue(command, out var operation))
{
    Console.WriteLine(operation(2, 5)); // 32
}
else
{
    Console.WriteLine("Əmr tapılmadı");
}

6. Faydalı incəliklər

Lambda-delegate: metoddan delegate qaytarmaq

Metodlardan delegate-lər (və yəni lambda-lar) qaytara bilərsən — demək olar ki, funksiya fabrikası çıxır!

Func<int, int> GetMultiplier(int factor)
{
    // factor-a vuracaq lambda-delegate qaytarırıq
    return x => x * factor;
}

var triple = GetMultiplier(3);
Console.WriteLine(triple(5)); // 15

var quadruple = GetMultiplier(4);
Console.WriteLine(quadruple(5)); // 20

Beləcə closure (bağlama) oyuncaqdan real alətə çevrilir!

Funksiyaların tərtibi: Combine, Delegate.Combine və multi-delegate-lər

Bəzən istəyirsən ki, bir çağırış bir neçə funksiyanı işə salsın. Məsələn, GUI-də düyməyə basdıqda bir neçə handler reaksiya verir.

Action və delegate-lər bir neçə funksiyanı “birləşdirməyə” imkan verir:

Action<string> pipeline = s => Console.WriteLine("Addım 1: " + s);
pipeline += s => Console.WriteLine("Addım 2: " + s.ToUpper());
pipeline += s => Console.WriteLine("Addım 3: " + s.Length);

pipeline("test");
// Çap edir:
// Addım 1: test
// Addım 2: TEST
// Addım 3: 4

Ssenari zəncirləri və event emal üçün əlverişlidir.

Diqqət: Dəyər qaytaran funksiyalar üçün nəticə yalnız sonuncu çağırışın nəticəsi olacaq — başlanğıc üçün sürpriz ola bilər. Action üçün isə hamısı işlənir, çünki onlar void qaytarır.

Lambda-delegate-ləri callback parametri kimi istifadə

Klassik ssenari — "işi bitirəndə çağır məni".

void DownloadFile(string url, Action<string> onFinish)
{
    // Fayl yüklənməsinin təqlidi...
    System.Threading.Thread.Sleep(500);// (Uzun işi təqlid edir, real GUI tətbiqlərində istifadə etməyin)
    onFinish($"Yükləmə '{url}' tamamlandı!");
}

DownloadFile("http://example.com", msg => Console.WriteLine(msg));

Real tətbiqlərdə (məsələn, API, DB, fayllarla işləyərkən) — bu standart pattern-dir.

Higher-order function: predicate ilə filter nümunəsi

Lambda-ifadələr və delegate-lər başqa funksiyaları parametr kimi qəbul edən universal funksiyalar yazmağa imkan verir. Bu higher-order function prinsiplərinin əsasıdır.

List<int> Filter(List<int> source, Predicate<int> condition)
{
    List<int> result = new List<int>();
    foreach (var item in source)
        if (condition(item)) result.Add(item);
    return result;
}

// Fərqli lambda-lardan istifadə:
var onlyPositive = Filter(new List<int> { -2, 0, 2, 7 }, x => x > 0);
var onlyEven = Filter(new List<int> { -2, 0, 2, 7 }, x => x % 2 == 0);

Hətta lambda fabrikləri yarada bilərsən:

Func<int, Predicate<int>> GetRangeChecker(int min, int max) =>
    x => x >= min && x <= max;

Predicate<int> inRange = GetRangeChecker(1, 10);

var filtered = Filter(new List<int> { 0, 5, 10, 15 }, inRange); // 5, 10

7. Birləşdirmə zamanı tipik səhvlər və incəliklər

Əgər parametr imzaları uyğun gəlməzsə, compiler şikayət edəcək (CS1660: "Cannot convert lambda expression").

Lambda-ifadə gözlənilməz şəkildə sonradan dəyişən variablları “yadda saxlaya” bilər (closure) — variable capture-a diqqət yetirin.

Dəyər qaytaran multi-delegate-lər yalnız sonuncu handler-in nəticəsini qaytarır — yeni başlayanlar üçün tez-tez sürpriz olur.

Expression tipli yerlərdə, məsələn Expression<Func<int,bool>>, statement-lambda qoymaq olmaz — mütləq expression-lambda tələb olunur.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Func və Action istifadəsi
Func və Action istifadəsi
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Delegatlar - lambda ifadələri üçün tip
Lambda ifadələri ilə delegatların əlaqəsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION