CodeGym /Kurslar /C# SELF /Lambda-ifadələr kolleksiyalarda və

Lambda-ifadələr kolleksiyalarda və LINQ

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

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
Where
Elementləri filtrasiya edir
Func<T, bool>
p => p.Price > 100
Select
Proyeksiya, dönüşüm
Func<T, U>
p => p.Name
OrderBy
Açar üzrə sıralama
Func<T, K>
u => u.Age
FirstOrDefault
Şərtə uyğun ilk element
Func<T, bool>
u => u.Username == "Bob"
Any
Şərtə uyğun ən azı bir element varmı
Func<T, bool>
u => u.Age < 18
All
Bütün elementlər şərti ödəyirmi
Func<T, bool>
u => u.Age >= 18
Count
Şərtə uyğun elementlərin sayı
Func<T, bool>
p => p.Price > 50
ForEach
Hər element üçün nəsə edir
Action<T>
u => Console.WriteLine(u.Name)
RemoveAll
Predikata uyğun bütün elementləri silir
Predicate<T>
u => u.Age < 18

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.

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