1. Lambda-ifadələrin üstünlükləri
Proqramlaşdırmada tez-tez oxşar tapşırıqlar təkrarlanır. Məsələn: "18 yaşdan yuxarı istifadəçiləri filterlə", "şərtə uyğun bütün ədədlərin cəmini hesabla", "malları qiymətə görə sırala". Lambda-lar olmadan belə tapşırıqları ayrıca metodlar yaradıram — bu isə kodda əlavə səs-küy olur, xüsusən də əgər əməliyyat yalnız bir yerdə istifadə olunursa. Lambda-lar kodu daha kompakt və problemi necə ifadə etdiyimizə daha yaxın edir.
Lambda-ifadələr müasir bir çox dillərdə standart vasitədir (yalnız C# deyil), çünki onlar "davranışı ötürməyə" imkan verir — filter, handler və ya transformasiya funksiyası kimi.
1. Qısa və lakonik yazı
Lambda-ifadələr əlavə metod və ya anonim delegate elanlarından xilas edir, xüsusən kiçik funksionallıq "uçuşda" yazılarkən. Məsələn, lambda-lar yaranmazdan əvvəl ədədlər siyahısını necə filterləyirdik:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
// Lambda-lar yaranmazdan əvvəl:
List<int> evenNumbers = numbers.FindAll(delegate(int x) { return x % 2 == 0; });
// Lambda-ifadə ilə:
List<int> evenNumbers2 = numbers.FindAll(x => x % 2 == 0);
Nəticə eynidir, amma lambda ilə kod çox daha kompakt olur. Böyük layihələrdə sətirlərin qənaəti əhəmiyyətli olur.
2. Oxunaqlılıq və ifadəlilik
Lambda-lar əməliyyatın mahiyyətinə fokuslanmağa imkan verir, sintaksis "səs-küyünü" aradan qaldırır. Kodunuz təbii dilə daha yaxın olur:
var adults = users.Where(user => user.Age >= 18);
Bunu ayrıca metod elan etməklə müqayisə edin: bool IsAdult(User user), onu sadəcə bu filter üçün yazmalı olacaqsınız.
3. LINQ və kolleksiya API-ları ilə rahat inteqrasiya
Lambda-ların əsas gücü LINQ və kolleksiyalarla birlikdədir. Standart kolleksiya metodlarının və LINQ operatorlarının çoxu parametr kimi funksiyanı gözləyir (məsələn, filter üçün Func<T, bool>). Lambda ilə lazım olan funksiyanı yerində elan etmək mümkündür:
var expensive = products.Where(p => p.Price > 1000);
var firstBook = books.FirstOrDefault(b => b.Title.StartsWith("C#"));
var doubled = numbers.Select(n => n * 2);
4. Xarici scope-dən dəyişənlərin tutulması (closures)
Lambda-ifadə xaricdə elan edilmiş dəyişənləri istifadə edə bilər. Bu elastiklik verir və uçuşda dinamik funksiyalar yaratmağa imkan yaradır:
int minAge = 18;
var filtered = users.Where(u => u.Age >= minAge); // minAge lambda tərəfindən "tutulur"
Bu "konfiqurasiya edilmiş parametrli" funksiyalar yaratmaq üçün maraqlı pattern-lər açır.
Maraqlı fakt: Lambda daxilində xarici dəyişəni yalnız oxumaqla kifayətlənməyib, bəzən onu dəyişmək də olar, amma bunu ehtiyatla etmək lazımdır — closures barədə mühazirədə daha çox danışacağıq!
5. Kontekstə yerləşdirilmiş kod
Lambda-ifadələr istifadə olunduqları yerdə "yaşayırlar", layihədə metodlar arasından axtarılmırlar. Bu kodu "possible information in minimal space" prinsiplərinə yaxınlaşdırır.
Bizim tətbiq nümunələrində (xatırladaq, kitabxana üçün mini-əsas izləmə sistemi hazırlayırıq), deyək ki, belə bir kitablar siyahımız var:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
public double Price { get; set; }
}
// Kodun hardasa:
List<Book> books = new List<Book>
{
new Book { Title = "C# 9.0 in a Nutshell", Author = "Skeet", Year = 2022, Price = 350 },
new Book { Title = "CLR via C#", Author = "Richter", Year = 2019, Price = 250 },
// ...
};
// 300-dən baha olan bütün kitabları tap:
var expensiveBooks = books.Where(b => b.Price > 300).ToList();
Seçim şərtlərini çağırışın yanına birbaşa görürsüz, başqa funksiyaları axtarmağa vaxt sərf etmirsiniz.
6. Callback-lər, event-lər, timer-lar üçün istifadə
Lambda-lar bir dəfəlik əməliyyat üçün əla uyğundur, məsələn event handler (callback):
button.Click += (sender, args) => Console.WriteLine("Düymə basıldı!");
Əgər handler çox sadədirsə, ayrıca metod yazmağa ehtiyac qalmır.
7. Delegate-lərin imkanlarını genişləndirmə
Əvvəllər davranışı ötürmək üçün adlandırılmış metodlar elan etmək lazım gəlirdi; indi isə yerində funksiya yazırsınız:
Timer timer = new Timer(_ => Console.WriteLine("Tik!"), null, 0, 1000);
8. Testləşdirmə və DI üçün sadələşdirmə
Lambda-larla test üçün mock davranışlar asanlıqla yaradıla bilər, əsas kodu köhnə util siniflərlə və ya müvəqqəti klasslarla çirkləndirmədən. Məsələn, əgər konstruktor delegate qəbul edirsə, test üçün istənilən davranışı verən lambda ötürə bilərsiniz.
2. Lambda-ifadələrin əsas çatışmazlıqları
Hər güclü alət kimi, lambda-ifadələr də problemlər yarada bilər. Gəlin onların yaratdığı çətinlikləri və məhdudiyyətləri müzakirə edək.
1. Həddindən artıq iç-içə olduqda oxunaqlılığın itməsi
Lambda-lar yaxşıdır, ta ki bir yerdə çox olmayınca. İç-içə lambda-lar və ya uzun lambda-lar kodu anlamsız edir:
var result = items.Select(x => x.Children.Where(y => y.Value > 10)
.Select(z => z.Name.ToUpper())
.ToList());
Bir-iki əlavə səviyyə əlavə edin — və "kod oxumaq üçün milli bulmaca" yaranır.
Məsləhət: Əgər lambdanız 3–4 sətrdən böyükdürsə — onu ayrı, adlandırılmış metoda çıxarın. Retro görünməkdən qorxmayın: oxunaqlılıq modaya uymaqdan daha vacibdir.
2. Debug etməkdə çətinliklər
Lambda-lar debugger-lə o qədər də dost deyil, xüsusən onlar bir sətirlik LINQ zəncirindədirsə. Bəzən lambda daxilində breakpoint qoymaq və ya konkret mərhələdə dəyişənləri görmək çətindir.
Debugu asanlaşdırmaq üçün lambdanın bədənini müvəqqəti adlandırılmış metoda çıxarın və ya uzun LINQ zəncirlərini ara dəyişənlərlə bölün.
3. Argument və dönüş tiplərinin qeyri-aydınlığı
Lambda tez-tez delegate kimi ötürülür (Func<...>, Action<...>, Predicate<T>). Bəzən giriş parametrlərinin və dönüş tipinin nə olması lazım olduğu dərhal aydın olmur, xüsusən generic metodlarda.
Məsələn:
Func<int, string, double> myFunc = (a, b) => a + b.Length; // Oops! int qaytaracaq, amma double olmalı idi.
Kompilyator səhvi göstərəcək, amma yeni başlayan üçün hansı lambda-nın "forma sığmadığını" dərhal başa düşmək çətin ola bilər.
4. Dəyişənlərin tutulması ilə bağlı problemlər
Xarici scope-dən dəyişənlərin tutulması (closure) — iki ucu kəsən qılıncdır. Tutulan dəyişənləri diqqətsiz istifadə etsəniz, gözlənilməz nəticə ala bilərsiniz. Məsələn, döngüdə:
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var action in actions) action();
Çoxu gözləyir 0 1 2, amma nəticə olur 3 3 3. Niyə? İcra vaxtına gəldikdə dəyişən i artıq 3-dür! Lambda dəyişənin özünü tutdu, onun dəyərini deyil.
Bu yeni başlayanlar üçün tipik səhvdur, rəsmi sənədlərdə (https://learn.microsoft.com/dotnet/csharp/language-reference/operators/lambda-expressions) ətraflı izah olunub. Həll mümkündür, amma diqqət tələb edir.
5. Adların itməsi və reuse problemleri
Lambda-lar bir dəfəyə uyğun əməliyyatlar üçün yaxşıdır. Eyni şərt/funksiya bir neçə yerdə istifadə olunursa, onu adlandırılmış metoda çıxarmaq daha yaxşıdır. Əks halda duplikasiya və düzəlişlər zamanı səhvlər yaranır.
6. XML-kommentar əlavə etmək çətinliyi
Lambda-ya XML-dokumentasiya əlavə etmək mümkün deyil (metodlarda olduğu kimi). Lambda-lar üçün şərhləri kod daxilində adi comment kimi yazmaq lazımdır.
7. Performansla bağlı potensial problemlər
Çox hallarda lambda-lar adi metodlardan hiss olunacaq dərəcədə yavaş deyil. Amma dəyişənləri tutan lambda-ların tez-tez və kütləvi yaradılması əlavə obyekt allokasiyasına (closure) səbəb olur. Performans kritik yerlərdə (məsələn, tight loop və ya yüksək yüklü xidmətlər) statik metodlardan istifadə etmək daha sərfəli ola bilər.
8. goto, break, continue operatorlarını xarici döngülərdə istifadə etmək olmaz
Əgər lambda döngü daxilində elan olunubsa, onun içindən xarici döngüyə birbaşa break və ya continue etmək mümkün deyil — sintaksis buna icazə vermir.
9. Lambda ilə hər şeyi təsvir etmək mümkün deyil
Lambda-lar atributlarla işləyə bilməz, erişim modifikatorları göstərmək olmaz, bəzi xüsusi əməliyyatları yerinə yetirmək mümkün deyil — məsələn, adlandırılmış local function kimi bəzi şeylər etmək olmur.
3. İki pislikdən seçim
Lambda-ifadələr nə vaxt faydalıdır
| Ssenari | Lambda — rahatdır? | Niyə |
|---|---|---|
| Qısa filter/transformasiya | 👍 | Tez və aydın |
| Çoxsəviyyəli iç-içə əməliyyatlar | 👎 | Oxunmaz olur |
| Təkrar istifadə (Re-use) | 👎 | Yaxşısı onu metoda çıxarmaqdır |
| Callback loqikası, event-lər | 👍 | Kompakt |
| Mürəkkəb biznes loqikasının təsviri | 👎 | Ad və şərhlər lazımdır |
| LINQ ilə işləmək | 👍 | Ideal ssenari |
Lambda-dan imtina edilməli hallarda
- Əgər loqika uzun və çox dallanmaları/hesablamaları əhatə edirsə.
- Əgər lambda oxuyan üçün qeyri-aydındır və izahı yoxdursa.
- Əgər funksiyanı sənədləşdirmək, bir neçə yerdə istifadə etmək və ya ona "danışan" ad vermək lazımdırsa.
- Əgər lambda çox dərin iç-içə çağırışlarda istifadə olunursa — oxunaqlılıq itə bilər.
4. Lambda-ifadələrlə işləyərkən tipik səhvlər
Döngüdə dəyişənlərin tutulması ilə bağlı səhv:
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var act in actions) act(); // Hamısı 5 çap edəcək!
Necə düzgün etmək olar:
for (int i = 0; i < 5; i++)
{
int captured = i; // ayrıca dəyişəni tuturuq
actions.Add(() => Console.WriteLine(captured));
}
Çox uzun lambda:
books.Where(b => b.Price > 1000 && b.Title.Contains("C#") && b.Author.Length > 4 && bla-bla-bla...);
// Kod oxunmaz oldu, bunu metoda çıxarın!
Lambda yerinə sənədləşdirilmiş metoda ehtiyac olduqda:
Əgər funksiya bir neçə dəfə istifadə olunacaqsa və ya onu ətraflı şərh etmək lazımdırsa, adlandırılmış metod yazmaq daha yaxşıdır:
bool IsExpensiveBook(Book book) => book.Price > 1000;
books.Where(IsExpensiveBook);
GO TO FULL VERSION