1. Giriş
Real layihələrdə demək olar ki, hər ikinci proqramçı kolleksiyalarla işləməyə həyatının xeyli hissəsini sərf edir: filtrasiya edir, sayır, axtarır, yenidən sıralayır, qoyur, çıxarır — ümumiyyətlə, gecə soyudancanın qarşısında olduğu kimi onlarla davranır. Xüsusilə çox vaxt məlumatları çıxarmaq, emal etmək və toplamaq lazımdır — istifadəçilər siyahıları, kataloqdakı məhsullar, mətn sətirləri və ya istənilən digər massivlər.
.NET-də demək olar ki, bütün müasir kolleksiyalar funksional metodları dəstəkləyir — məsələn, Where, Select, Find, Any, All və s. Onların gücü — ümumi və yığcam üslubda: siz sadəcə "mantıq parçasını" lambda-ifadəsi kimi verirsiniz və kolleksiya canlanır, elə bil yeni motor işə düşüb.
LINQ (Language Integrated Query) — bu sadəcə sintaktik şirindir, yox, C# daxilində kiçik bir dilidir, məlumatlara sorğu yazmağı SQL və ya Excel istifadə edirmiş kimi imkan verir. Amma daha yaxşı: düz kodda, autocompletion, tiplər və debugger ilə.
Amma bütün bu sehr delegatların hesabına işləyir — və hər dəfə massiv üçün filtrasiya məqsədilə ayrıca metod yazmaq yorucudur. Burada lambda-ifadələr "yerində" mini-funksiya kimi rol oynayır və ağır kodu zərif və ifadəli edir.
2. Lambda-ifadələr standart kolleksiya metodlarında
Lambda-ifadələr özünü xüsusilə yaxşı göstərir standart kolleksiya metodlarında, delegatlara əsaslanan, məsələn, Find, Exists, ForEach və bir çox başqalarında.
Nümunə: Şərtə görə axtarış
Tutaq ki, sizin məhsullar siyahınız var:
using System;
using System.Collections.Generic;
// Bizim məhsul sinifimiz
public class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
var products = new List<Product>
{
new Product { Name = "Kofe", Price = 100 },
new Product { Name = "Çay", Price = 70 },
new Product { Name = "Süd", Price = 80 }
};
// İlk bahalı məhsulu tapaq (>90)
Product expensive = products.Find(p => p.Price > 90); // Lambda istifadə edirik!
Console.WriteLine(expensive?.Name); // => Kofe
Lambdasız ayrıca metod və ya köhnə stil anonim funksiya yazmalı olardınız. Belə isə — bir sətir və kod ingilis kimi oxunur: "Tap məhsulu, harada qiymət 90-dan böyükdür".
Nümunə: Məhsulun mövcudluğunu yoxlamaq
bool hasCheap = products.Exists(p => p.Price < 75);
Console.WriteLine(hasCheap); // => True (çünki "Çay" 75-dən ucuzdur)
Nümunə: Bütün elementlərin emalı (ForEach)
Bəzən hər elementlə nəsə etmək lazımdır:
products.ForEach(p => Console.WriteLine($"{p.Name}: {p.Price} avro"));
Analogiyalar barədə
Qısa: kolleksiyalarda lambda-ifadələr — fotoredaktorda "gözəlləşdir" düyməsi kimidir. Basırsan — nəticə var!
3. Lambda-ifadələr və LINQ: kolleksiyalar üçün sehr
LINQ yalnız rahatlıq deyil, həm də funksional tərzə gözəl girişdir. Əksər LINQ-metodları delegat gözləyir — deməli onların ideal dostu və yoldaşı lambda-ifadələrdir.
Where ilə filtrasiya
Yenə məhsullar siyahımız var. İndi yalnız "ucuz" məhsulları seçək:
using System.Linq;
var cheapProducts = products.Where(p => p.Price < 90);
foreach (var p in cheapProducts)
Console.WriteLine(p.Name); // Çay, Süd
Yeni kolleksiya əldə etdik, bir döngü belə yazmadan. Where lambda-predikat (true/false qaytaran funksiya) qəbul edir və onu hər elementə tətbiq edir.
OrderBy ilə sıralama
Sıra sevənlər üçün:
var sorted = products.OrderBy(p => p.Price);
foreach (var p in sorted)
Console.WriteLine($"{p.Name}: {p.Price}");
// Çay: 70
// Süd: 80
// Kofe: 100
Mapping (Select) — məlumatların proyeksiyası
Bəzən bizə bütün obyekt yox, onun yalnız bir hissəsi lazımdır, məsələn, məhsul adlarının siyahısı:
var names = products.Select(p => p.Name);
foreach (var name in names)
Console.WriteLine(name); // Kofe, Çay, Süd
LINQ zəncirləri
LINQ yaxşıdır ki, çağırışları bir-birinə "zəncirləmək" olar:
var namesOfCheap = products
.Where(p => p.Price < 90)
.OrderBy(p => p.Name)
.Select(p => p.Name.ToUpper());
foreach (var name in namesOfCheap)
Console.WriteLine(name); // SÜD, ÇAY
Bu istehsal xətti zənciri kimidir: hər metod — yeni emal mərhələsi.
Sual: Lambda-ifadələr niyə adi metodlardan yaxşıdırlar LINQ üçün?
Birincisi, lambda-ları lazım olduğu yerdə yazmaq olar. İkincisi, lambda-ifadələr qısadır və oxunaqlıdır. Üçüncüsü, bu müasir C#-ın standartıdır — hamı belə yazır, yazmayanlar adətən müsahibədən keçə bilmir.
4. Praktiki nümunə
Kurs ərzində kiçik kataloq — məhsullar, istifadəçilər və ya sifarişlərlə işləyən tədris tətbiqi yazırdıq. Gəlin ora müasir kolleksiya emal metodları əlavə edək.
İstifadəçini ada görə axtarmaq
public class User
{
public string Username { get; set; }
public int Age { get; set; }
}
var users = new List<User>
{
new User{ Username = "Alice", Age = 21 },
new User{ Username = "Bob", Age = 26 },
new User{ Username = "Charlie", Age = 32 }
};
// Ada görə istifadəçi axtarışı
User found = users.FirstOrDefault(u => u.Username == "Bob");
Console.WriteLine(found?.Age); // 26
Yaşa görə filtrasiya
var adults = users.Where(u => u.Age >= 18);
foreach (var u in adults)
Console.WriteLine(u.Username); // Alice, Bob, Charlie
İstifadəçilərin sayını hesablamaq
int count = users.Count(u => u.Age > 25);
Console.WriteLine(count); // 2 (Bob və Charlie)
Bütün istifadəçilərin yetkin olmasını yoxlamaq
bool allAdults = users.All(u => u.Age >= 18);
Console.WriteLine(allAdults); // True
Hər hansı bir azyaşlı varmı?
bool hasMinor = users.Any(u => u.Age < 18);
Console.WriteLine(hasMinor); // False
5. LINQ: bunlar içəridən necə işləyir
Məsələn, Where(u => u.Age > 20) yazdığınız zaman, əslində bu təxminən bütün elementləri gəzib hər biri üçün şərti yoxlayan döngü yaratmaq kimidir. Sadəcə LINQ bunu görünməz və gözəl edir, sizin predikatınızı delegat kimi bükərək.
Əgər lambda-ifadələr olmasaydı, belə strukturlar düzəltməli olardıq:
public static bool AgeMoreThan20(User u) => u.Age > 20;
var adultUsers = users.Where(AgeMoreThan20);
Yaxud tamamilə "oldschool" anonim metod:
var adultUsers = users.Where(delegate(User u) { return u.Age > 20; });
Bunların hamısı həcmlidir və darıxdırıcıdır. Lambda ilə — zərif və müasir.
6. Delegatlar və standart tiplər: Func, Action, Predicate
Lambda-ifadələr yalnız LINQ-u sevmir. Bir çox standart kolleksiya metodları ixtisaslaşmış delegatları qəbul edir, məsələn:
- Predicate<T> — Find, Exists, RemoveAll kimi metodlar üçün
- Func<T, TResult> — LINQ-metodlar, proyeksiyalar, hesablamalar üçün
- Action<T> — elementlə nəsə edən, amma heç nə qaytarmayan metodlar üçün (ForEach)
Praktikada bu belə görünür:
// Predicate<T>
users.RemoveAll(u => u.Age < 30); // 30-dan kiçikləri sildik
// Func<T, TResult>
var names = users.Select(u => u.Username);
// Action<T>
users.ForEach(u => Console.WriteLine(u.Username));
7. Lambda-ifadəli kolleksiya metodlarının yaddaş dəftəri
| Metod | Nə edir | Delegat tipi | Lambda nümunəsi |
|---|---|---|---|
|
Elementləri filtrasiya edir | |
|
|
Proyeksiya, dönüşüm | |
|
|
Açar üzrə sıralama | |
|
|
Şərtə uyğun ilk element | |
|
|
Şərtə uyğun ən azı bir element varmı | |
|
|
Bütün elementlər şərti ödəyirmi | |
|
|
Şərtə uyğun elementlərin sayı | |
|
|
Hər element üçün nəsə edir | |
|
|
Predikata uyğun bütün elementləri silir | |
|
8. Tipik səhvlər və xüsusiyyətlər
Ən çox rast gəlinən səhvlərdən biri — unutmaqdır ki, LINQ ilkin kolleksiyanı dəyişmir, yeni ardıcıllıq qaytarır. Yəni, var sorted = users.OrderBy(u => u.Age); kodundan sonra users ilkin sıralamada qalacaq! Bu qarışıqlığa səbəb ola bilər: bəzən elə gəlir ki, hər şey artıq sıralanıb — amma əslində yox.
Daha bir nüans: Where, Select və digər metodlar IEnumerable<T> tipində obyektlər qaytarır. Bu "laizy" kolleksiyadır — əsl emal onda başlayır ki, siz həqiqətən enumerasiya etməyə başlayırsınız (foreach, ToList() və s.). Ona görə nəticəni materializə etmək istəyirsinizsə, ToList() və ya ToArray() çağırmağı unutmayın:
var sortedList = users.OrderBy(u => u.Age).ToList();
Həmçinin yadda saxlayın: əgər lambda-ifadə xarici scope-dakı dəyişənlərə istinad edirsə (closure), bu dəyişənlər lambda-ya olan istinad yaşadığı müddətcə yaddaşda "yaşayır". Çox qorxulu deyil, amma əgər siz uzunömürlü obyekt daxilində lambda istifadə edib orada "əhəmiyyətli böyük massiv" tutmusunuzsa, həmin massiv lambda ilə birlikdə yaddaşda qalacaq.
Və daha: parametr və dəyişənlərə aydın adlar verin — bu oxunaqlığı ciddi artırır, xüsusən bir neçə səviyyə iç-içə lambda olduqda.
GO TO FULL VERSION