CodeGym /Kurslar /C# SELF /Lambdda-ifadələrin ( =>

Lambdda-ifadələrin ( =>) parametr kimi ötürülməsi

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

1. Giriş

İndiyə kimi siz gördünüz: biz LINQ metodlarını çağırırdıq, hansı ki lambdda qəbul edirdi. Amma C# metodu necə anlayır ki, ona funksiyanı ötürmək olar?

Sadə desək, delegatlar funksiyalar üçün "interfeys" kimidir: biz imzanı (parametrlərin tipləri və geri qaytarılan dəyər) təsvir edirik və bu imzaya uyğun hər hansı bir metod (və ya lambdda!) o yerlərə ötürülə bilər ki, orada həmin delegat gözlənilir.

Xatırlayın, siz necə string-i parametr kimi ötürürdünüz? Təxminən eyni şəkildə “loqika” ötürülür, yalnız parametr tipi — delegat olur.

Delegatlar: insan dilində əsas nəzəriyyə

C#-da delegat — bu “belə imzalı funksiya”nı təsvir edən tipdir.

// Delegat, int qəbul edir və bool qaytarır
public delegate bool IntPredicate(int x);

İmzası uyğun olan hər hansı bir funksiya bu tipdə dəyişənə təyin oluna bilər:

bool IsEven(int n) => n % 2 == 0;

IntPredicate pred = IsEven;

Və indi — lambdda uyğun gəlir:

IntPredicate pred = x => x % 2 == 0;

Universal delegatlar: Func, Action, Predicate

  • Func<T1, ..., TResult>T1, ... parametrlərini qəbul edən və TResult-u qaytaran funksiya.
  • Action<T1, ...> — parametrlər qəbul edən və dəyər qaytarmayan funksiya (void).
  • Predicate<T>T qəbul edən və bool qaytaran funksiya.

2. Lambdda-nı öz metodumuza ötürmə

Tutaq ki, biz dərs üçün kiçik konsol tətbiqimizi inkişaf etdiririk — istifadəçilər siyahısı ilə işləyən console layihəsi. Əvvəl kolleksiyaları LINQ ilə filtrləyirdik, indi isə lambdda-şərt qəbul edən öz metodumuzu yazacağıq.

Lambdda-parametri olan metod yaradırıq

// Nümunə üçün User sinifini təyin edək (tətbiqimizə əlavə edirik)
public class User
{
    public string Name { get; set; }
    public bool IsActive { get; set; }
}

// Siyahını və delegat-şərti (lambdda) qəbul edən metod
public static List<User> FilterUsers(List<User> users, Predicate<User> predicate)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (predicate(user)) // Lambdda çağırılır!
            result.Add(user);
    }
    return result;
}

İndi hər hansı lambdda ötürmək olar:

var users = new List<User>
{
    new User { Name = "Vasya", IsActive = true },
    new User { Name = "Petya", IsActive = false },
    new User { Name = "Masha", IsActive = true }
};

// Yalnız aktivləri filtr edirik
var activeUsers = FilterUsers(users, user => user.IsActive);

foreach (var user in activeUsers)
    Console.WriteLine(user.Name); // Vasya, Masha

Beləliklə! Biz loqika parçasını — kiçik funksiyanı — adi parametr kimi ötürdük, çünki FilterUsers Predicate<User> gözləyir və biz ona uyğun lambdda verdik.

Func<T, TResult> ilə variant

Predicate<T> şərt üçün uyğundur (geri qaytarılan bool). Amma əgər hər bir istifadəçi üçün nəsə “hesablamaq” istəyiriksə?

// Hər elementə funksiya tətbiq edib nəticələri toplayan metod
public static List<TResult> MapUsers<TResult>(List<User> users, Func<User, TResult> selector)
{
    var result = new List<TResult>();
    foreach (var user in users)
    {
        result.Add(selector(user));
    }
    return result;
}

İstifadə:

var names = MapUsers(users, user => user.Name.ToUpper());

foreach (var name in names)
    Console.WriteLine(name); // VASYA, PETYA, MASHA

3. Faydalı nüanslar

Müxtəlif ötürmə formaları

Lambdda ilə yanaşı adi metodu da ötürmək olar — əsas odur ki, imza uyğun olsun.

// Adi metod
static bool NameHasS(User user) => user.Name.Contains("s");

// Adi metodun ötürülməsi:
var usersWithS = FilterUsers(users, NameHasS);
// Lambddanın ötürülməsi
var usersWithA = FilterUsers(users, u => u.Name.Contains("a"));

Həmçinin köhnə tip anonim metod da mümkündür (amma buna ehtiyac yoxdur):

var usersWithM = FilterUsers(users, delegate(User u) { return u.Name.Contains("m"); });

Müasir stil — lambddalardır!

LINQ-də lambdda ötürülməsi: arxada nə baş verir

var result = users.Where(u => u.IsActive).ToList();

Arxa planda Where Func<User, bool> qəbul edir. Bu o deməkdir ki, hər hansı metod ki, Func<...> qəbul edirsə, eyni cür istifadə etmək olar!

İki parametr istəyirsinizsə?

// Metod iki lambdda qəbul edir filtr üçün
public static List<User> FilterUsersCustom(
    List<User> users,
    Func<User, bool> include,
    Func<User, bool> exclude)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (include(user) && !exclude(user))
            result.Add(user);
    }
    return result;
}

İstifadə:

var customFiltered = FilterUsersCustom(
    users,
    u => u.Name.StartsWith("V"),
    u => u.IsActive == false
);
// Yalnız adı "V" ilə başlayan və aktiv olan istifadəçiləri götürəcək

Ssenari: Filtrlər fabriki

Console.WriteLine("Adın minimal uzunluğunu daxil edin:");
int minLength = int.Parse(Console.ReadLine());

Predicate<User> lengthFilter = user => user.Name.Length >= minLength;
var filteredUsers = FilterUsers(users, lengthFilter);

// Çox interaktiv və canlı!

4. Tipik səhvlər və nüanslar

Bəzən kompilyator lambdda parametrlərinin tipini “çıxaara” bilmir — xüsusən overload-ların olduğu mürəkkəb səhnarilərdə və ya metod konkret geri qaytarma tipli delegat tələb etdikdə. Belə hallarda lambdda tiplərini açıq göstərmək olar:

FilterUsers(users, (User u) => u.Name.Length > 3);

və ya hətta:

MapUsers(users, (User u) => u.Name.ToUpper());

Səhv: lambdda imzaya uyğun gəlmir

FilterUsers(users, user => Console.WriteLine(user.Name)); // səhv! bool gözlənilir, void alınır

Çünki bool qaytaran funksiya gözlənilir, amma lambdda void qaytarır (dəqiq desək, heç nə qaytarmır). Geri qaytarılan tipə diqqətli olun!

Səhv: lambddalardan artıq istifadə

Əgər lambddalar 10 sətrə qədər uzanırsa, onları ayrıca metoda çıxarın. Bu həm oxunaqlıdır, həm də debugging üçün daha rahatdır.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION