1. Giriş
Maraqlıdır, lambda-ifadə özü təmiz resept kimidir — qab olmadan. O nə edəcəyini təsvir edir, amma kodda yaşaması üçün "konteyner"ə ehtiyacı var. C#-da bunun üçün hazır universal delegat tipləri var: Func, Action və Predicate.
Onları lambda-larınız üçün hazır qəliblər kimi təsəvvür edin — lazım olanı götürün və loqikanızı tökün. Özəl delegat tipləri yazmağa çox vaxt ehtiyac qalmır, çünki lazım olan artıq əl altında var.
Bir az tarixçə
İlkin vaxtlarda funksiyanı parametr kimi ötürmək lazım olanda öz delegat tipinizi elan etmək lazım gəlirdi. Bu uzun, sıxıcı idi və belə görünürdü:
delegate int Calculate(int x, int y);
Calculate adder = (a, b) => a + b;
C#-da Func, Action və Predicate ortaya çıxandan sonra əksər hallarda öz delegatlarımızı əl ilə yazmağı unutmaq mümkün oldu. İndi daha sadə və universal görünür:
Func<int, int, int> adder = (a, b) => a + b;
2. Func<T, TResult> — qaytarışı olan funksiya
Sintaksis və məqsəd
Func — generic delegatdır, 0-dan 16-ya qədər parametr qəbul edə bilər və dəyər qaytarır.
Func<int, int, int> sum = (x, y) => x + y;
Func<tip1, tip2, ..., tipN, TResult> — sondakı tip qaytarılan tipdir, digər bütün tiplər arqumentlərdir.
Nümunələr
1. İki ədədin cəmi
Func<int, int, int> sum = (x, y) => x + y;
Console.WriteLine(sum(3, 5)); // 8
2. Ədədin kvadrata qaldırılması
Func<int, int> square = x => x * x;
Console.WriteLine(square(4)); // 16
3. Parametrsiz
Func<string> greet = () => "Privet, lambda!";
Console.WriteLine(greet());
Vizual sxema
| İmza | Nümunə | Təsvir |
|---|---|---|
|
|
Bir int qəbul edir, int qaytarır |
|
|
İki int, int qaytarır |
|
|
Heç nə qəbul etmir, string qaytarır |
Tətbiqinizdə necə görünür
Farz edək ki, kiçik bir tətbiqimiz var ("Istifadəçi kataloqu"), siyahımızda ədədlər var — onlara müxtəlif emal tətbiq etmək istəyirik. Məsələn, kvadrata qaldırmaq və ya sabit ədədlə cəmləmək:
List<int> numbers = new() { 1, 2, 3, 4, 5 };
Func<int, int> square = x => x * x;
var squares = numbers.Select(square);
Console.WriteLine(string.Join(", ", squares)); // 1, 4, 9, 16, 25
3. Action<T> — qaytarışı olmayan əməliyyat
Action — bir şey edən (məsələn, ekrana çap edən) amma heç nə qaytarmayan metodlar üçün universal delegatdır.
0-dan 16-ya qədər parametr qəbul edə bilər, amma həmişə void qaytarır.
Nümunələr
1. Ekrana çap
Action<string> print = text => Console.WriteLine("Dannye: " + text);
print("Privet, mir!");
2. Parametrsiz əməliyyat
Action greet = () => Console.WriteLine("Dobro pozhalovat'!");
greet();
3. Bir neçə parametrli əməliyyat
Action<int, int> showSum = (a, b) => Console.WriteLine($"Summa: {a + b}");
showSum(2, 3); // Summa: 5
Vizual sxema
| İmza | Nümunə | Təsvir |
|---|---|---|
|
|
Parametrsiz, nəticəsiz |
|
|
Bir parametr |
|
|
Bir neçə parametr |
Tətbiqimizdə
Istifadəçi kataloquna bütün adları çap edən metod əlavə edək:
List<string> names = new() { "Anna", "Boris", "Vika" };
Action<string> printName = name => Console.WriteLine("Pol'zovatel': " + name);
names.ForEach(printName);
// və ya belə: names.ForEach(name => Console.WriteLine(name));
4. Predicate<T> — Bəli yox?
Bir parametr üçün yalnız true və ya false qaytaran funksiya lazım olduqda, Predicate<T>-dən istifadə edin. Bu, bir parametr qəbul edən və bool qaytaran delegatdır.
Predicate — praktik olaraq "bizə boolean yoxlama lazımdır" üçün olan qutu xarakterli delegatdır.
Nümunələr
1. 5-dən böyük olub-olmadığını yoxlamaq
Predicate<int> isGreaterThanFive = x => x > 5;
Console.WriteLine(isGreaterThanFive(3)); // false
Console.WriteLine(isGreaterThanFive(7)); // true
2. List<T>.Find ilə istifadə
List<int> values = new() { 2, 4, 7, 10 };
int found = values.Find(isGreaterThanFive); // Predicate<int> istifadə edilir
Console.WriteLine(found); // 7
3. Hamısı yetkinlərmi?
List<int> ages = new() { 12, 19, 34 };
bool allAdults = ages.TrueForAll(age => age >= 18);
// TrueForAll Predicate<int> qəbul edir
Func<T, bool>-dan fərqi nədir?
Əslində, onlar demək olar ki, eynidır və bir-birinin yerinə istifadə edilə bilərlər. Microsoft sənədləri belə deyir: "Predicate<T> — sadəcə xüsusi API-lər üçün Func<T, bool>-un ekvivalentidir". Amma standart kitabxana metodları bəzən məhz Predicate-i tələb edir.
5. Lambda-ifadələri necə Func, Action, Predicate-ə "sığır"
Lambda yazanda C# bunu analiz edir: "Bu forma tələb olunan delegata uyğundur — buraya qoymaq olar!"
Func<int, int> f1 = x => x * 2;
Action<string> a1 = text => Console.WriteLine(text);
Predicate<int> p1 = x => x < 10;
Hər yerdə lambda var! Amma daxildə — fərqli imzalı üç müxtəlif delegat dayanır.
"Real" kod nümunəsində tətbiq
List<User> users = new() {
new User("Anna", 24),
new User("Boris", 17),
new User("Vika", 31),
};
// Yalnız yetkin istifadəçiləri qaytaran funksiya (Predicate<User>)
List<User> adults = users.FindAll(user => user.Age >= 18);
Console.WriteLine("Spisok vzroslyh: " + string.Join(", ", adults.Select(u => u.Name)));
Və əgər bütün istifadəçilərin adlarını Action<User> ilə çap etmək istəyiriksə:
users.ForEach(user => Console.WriteLine(user.Name));
Adlarını almaq üçün (Func<User, string>):
IEnumerable<string> names = users.Select(user => user.Name);
Aydınlaşdırıcı cədvəl
| Delegat | İmza | Lambda nümunəsi | Harada istifadə olunur |
|---|---|---|---|
|
T → U | |
Select, hər cür transformasiyalar |
|
T → void | |
ForEach, əməliyyat metodları |
|
T → bool | |
Find, Exists, filtrlər |
6. Daxili tətbiq nümunələri: addım-addım
Gəlin "Istifadəçi kataloqu" kiçik tətbiqimizi genişləndirək. Gəlin bir User sinifimiz olsun:
public class User
{
public string Name { get; }
public int Age { get; }
public bool IsActive { get; set; }
public User(string name, int age)
{
Name = name;
Age = age;
IsActive = true;
}
}
1. Func<User, bool> — istifadəçinin yetkin olub-olmadığını yoxlayırıq
Func<User, bool> isAdult = user => user.Age >= 18;
LINQ-də istifadə edək:
var adults = users.Where(isAdult);
2. Predicate<User> — aktiv istifadəçini axtarırıq
Predicate<User> isActive = user => user.IsActive;
User found = users.Find(isActive);
3. Action<User> — istifadəçini deaktiv edirik
Action<User> deactivate = user => user.IsActive = false;
users.ForEach(deactivate);
4. Func<User, string> — qısa təsvir alırıq
Func<User, string> describe = user => $"{user.Name} ({user.Age})";
var descriptions = users.Select(describe);
Bütün bu lambda-lar "məlumat kimi kod"durlar — onları metodlara ötürmək, dəyişkənlərdə saxlamaq və kombinə etmək olar.
7. Gözə dəyməyən nüanslar və tipik səhvlər
1. Lambda delegatın imzasına uyğun olmalıdır. Əgər imza uyğun gəlməzsə, kompilyasiya xətası olacaq.
Func<int, string> wrong = x => x * 2; // xəta: string gözlənilir, int alındı
// Düzgünü:
Func<int, string> right = x => (x * 2).ToString();
2. void vs return unutmayın. Action heç nə qaytarmır — məsələn Action<int> a = x => x * x; işləməyəcək, çünki dəyər qaytarılır, amma qaytarılmamalıdır.
3. Predicate<T> və Func<T, bool> tez-tez bir-birinin yerinə istifadə edilə bilir, amma hər zaman yox. Bəzən kolleksiya metodları məhz Predicate<T>-i tələb edir, bəzən isə Func<T, bool>-ı. Birbaşa təyin etmə hər zaman işləməyə bilər.
Predicate<int> pred = x => x > 0;
Func<int, bool> func = pred; // xəta
// Amma:
Func<int, bool> func2 = x => x > 0;
Predicate<int> pred2 = new Predicate<int>(func2); // constructor vasitəsilə çevirmək olar
GO TO FULL VERSION