CodeGym /Kurslar /C# SELF /Lambda-ifadələri ilə Func, Action, Predicate

Lambda-ifadələri ilə Func, Action, Predicate

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

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
Func<int, int>
x => x * 2
Bir int qəbul edir, int qaytarır
Func<int, int, int>
(a, b) => a + b
İki int, int qaytarır
Func<string>
() => "hi"
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
Action
() => ...
Parametrsiz, nəticəsiz
Action<int>
x => ...
Bir parametr
Action<int, string>
(x, s) => ...
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
Func<T, U>
T → U
user => user.Name
Select, hər cür transformasiyalar
Action<T>
T → void
user => Console.WriteLine(...)
ForEach, əməliyyat metodları
Predicate<T>
T → bool
user => user.Age > 18
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
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
`Action` istifadə edərək sətirlər siyahısının işlənməsi
`Action` istifadə edərək sətirlər siyahısının işlənməsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION