16-bo‘lim

Delegatlar, lambda va hodisalar

Delegat nima, Func va Action, lambda ifodalar, yopilmalar, metodni parametr sifatida uzatish va hodisalar bilan ishlash.

🕑 11 daqiqa o‘qish 📄 589 so‘z 👁 1 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Func va Action
  2. Lambda ifodalar
  3. Metodni parametr sifatida uzatish
  4. Yopilma
  5. Metod guruhi
  6. Bir nechta metodni bog'lash
  7. Hodisalar
  8. Amaliy misol: saralash mezoni
  9. Xulosa

Delegat - metodga havola. Ya'ni metodni o'zgaruvchiga saqlash, parametr sifatida uzatish va qaytarish mumkin.

LINQ ning butun kuchi shunga asoslangan.

Func va Action #

Amalda o'z delegat turingizni e'lon qilish kamdan-kam kerak - tayyor ikkitasi yetarli:

CSHARP
Func<int, int> kvadrat = son => son * son;
Func<int, int, int> qoshish = (a, b) => a + b;
Action<string> yozish = matn => Console.WriteLine($"[LOG] {matn}");

Console.WriteLine(kvadrat(7));
Console.WriteLine(qoshish(3, 4));
yozish("dastur ishga tushdi");
Natija
49
7
[LOG] dastur ishga tushdi
TurQaytaradimiMisol
ActionYo'qAction<string> - satr oladi, hech narsa qaytarmaydi
FuncHaFunc<int, int> - int oladi, int qaytaradi
Predicate<T>boolFunc<T, bool> bilan bir xil
Func da oxirgi tur - qaytish turi

Func<int, string, bool> ni shunday o'qing: int va string oladi, bool qaytaradi.

Ya'ni oxirgisi har doim natija turi, qolganlari - parametrlar. Bu boshida chalkash tuyuladi, lekin tez odat bo'lib qoladi.

Action da esa hammasi parametr, chunki u hech narsa qaytarmaydi.

Lambda ifodalar #

Lambda - nomsiz metodning qisqa yozuvi:

CSHARP
Func<int, int> a = son => son * 2;
Func<int, int> b = (int son) => son * 2;
Func<int, int> c = son =>
{
    int natija = son * 2;
    return natija;
};

Console.WriteLine($"{a(5)}, {b(5)}, {c(5)}");
Natija
10, 10, 10

Uchala yozuv ham bir xil. Birinchisi eng qisqa va eng ko'p ishlatiladigani.

Metodni parametr sifatida uzatish #

Bu delegatlarning asosiy foydasi:

CSHARP
int[] sonlar = [5, 12, 8, 3, 20, 15];

Console.WriteLine(string.Join(", ", Filtrla(sonlar, s => s > 10)));
Console.WriteLine(string.Join(", ", Filtrla(sonlar, s => s % 2 == 0)));
Console.WriteLine(string.Join(", ", Filtrla(sonlar, s => s < 6)));

List<int> Filtrla(int[] manba, Func<int, bool> shart)
{
    List<int> natija = [];
    foreach (int son in manba)
    {
        if (shart(son))
        {
            natija.Add(son);
        }
    }
    return natija;
}
Natija
12, 20, 15
12, 8, 20
5, 3

Bitta Filtrla metodi uchta turli shart bilan ishladi. Shartni o'zgartirish uchun metodni qayta yozish kerak emas.

Aynan shunday LINQ ning Where metodi qurilgan.

Yopilma #

Lambda o'zi turgan joydagi o'zgaruvchilarni eslab qoladi:

CSHARP
int chegara = 10;

Func<int, bool> kattami = son => son > chegara;

Console.WriteLine($"chegara={chegara}: 15 kattami? {kattami(15)}");

chegara = 20;
Console.WriteLine($"chegara={chegara}: 15 kattami? {kattami(15)}");
Natija
chegara=10: 15 kattami? True
chegara=20: 15 kattami? False

Lambda chegara ning nusxasini emas, o'zini ushlab turadi. Shuning uchun uni o'zgartirganda lambda natijasi ham o'zgardi.

Siklda yopilma - klassik tuzoq
CSHARP
List<Func<int>> yomon = [];
for (int i = 0; i < 3; i++)
{
    yomon.Add(() => i);
}

List<Func<int>> yaxshi = [];
for (int i = 0; i < 3; i++)
{
    int nusxa = i;
    yaxshi.Add(() => nusxa);
}

Console.WriteLine(
    $"Sikl o'zgaruvchisi: {string.Join(", ", yomon.Select(f => f()))}");
Console.WriteLine(
    $"Nusxa bilan:        {string.Join(", ", yaxshi.Select(f => f()))}");
Natija
Sikl o'zgaruvchisi: 3, 3, 3
Nusxa bilan:        0, 1, 2

Birinchi holatda uchala lambda ham bitta i o'zgaruvchisini ushlaydi va sikl tugaganda uning qiymati 3 bo'lib qolgan.

Yechim: sikl ichida yangi o'zgaruvchi yarating.

Diqqat: foreach siklida bu muammo yo'q - C# 5 dan beri u har takrorda yangi o'zgaruvchi yaratadi. Muammo faqat for da.

Metod guruhi #

Lambda yozmasdan, mavjud metodni to'g'ridan-to'g'ri berish mumkin:

CSHARP
string[] sozlar = ["olma", "behi", "uzum"];

// lambda bilan
sozlar.ToList().ForEach(s => Console.WriteLine(s));

Console.WriteLine("---");

// metod guruhi - qisqaroq
sozlar.ToList().ForEach(Console.WriteLine);
Natija
olma
behi
uzum
---
olma
behi
uzum

Bir nechta metodni bog'lash #

Delegatga bir nechta metod qo'shish mumkin - ular ketma-ket chaqiriladi:

CSHARP
Action<string> xabar = Konsolga;
xabar += Jurnaliga;
xabar += Ekranga;

xabar("Tizim ishga tushdi");

Console.WriteLine("---");

xabar -= Jurnaliga;
xabar("Ikkinchi xabar");

void Konsolga(string m) => Console.WriteLine($"konsol: {m}");
void Jurnaliga(string m) => Console.WriteLine($"jurnal: {m}");
void Ekranga(string m) => Console.WriteLine($"ekran:  {m}");
Natija
konsol: Tizim ishga tushdi
jurnal: Tizim ishga tushdi
ekran:  Tizim ishga tushdi
---
konsol: Ikkinchi xabar
ekran:  Ikkinchi xabar

Hodisalar #

Hodisa (event) - delegatning himoyalangan ko'rinishi. U "biror narsa yuz berdi" deb xabar berish uchun ishlatiladi:

CSHARP
var hisob = new Hisob("Husanboy");

hisob.PulYechildi += miqdor =>
    Console.WriteLine($"  SMS: {miqdor} so'm yechildi");
hisob.PulYechildi += miqdor => Console.WriteLine($"  Jurnal: -{miqdor}");

hisob.Yechish(50000);
hisob.Yechish(2000000);

class Hisob(string egasi)
{
    public event Action<decimal>? PulYechildi;

    public decimal Balans { get; private set; } = 1000000;

    public void Yechish(decimal miqdor)
    {
        if (miqdor > Balans)
        {
            Console.WriteLine($"{egasi}: mablag' yetarli emas");
            return;
        }

        Balans -= miqdor;
        Console.WriteLine(
            $"{egasi}: {miqdor} so'm yechildi, qoldiq {Balans}");
        PulYechildi?.Invoke(miqdor);
    }
}
Natija
Husanboy: 50000 so'm yechildi, qoldiq 950000
  SMS: 50000 so'm yechildi
  Jurnal: -50000
Husanboy: mablag' yetarli emas
event va oddiy delegat farqi

Maydonni event deb belgilash ikki narsani taqiqlaydi:

  1. Tashqaridan butunlay almashtirish (hisob.PulYechildi = ...) mumkin emas - faqat += va -=;
  2. Tashqaridan chaqirish mumkin emas - faqat sinf o'zi Invoke qila oladi.

Bu muhim: aks holda boshqa kod obunachilarni tasodifan o'chirib yuborishi yoki hodisani soxta chaqirishi mumkin edi.

Hodisani chaqirishdan oldin null ni tekshiring

Hech kim obuna bo'lmagan hodisa null bo'ladi. Uni to'g'ridan- to'g'ri chaqirsangiz - NullReferenceException.

Shuning uchun yuqorida PulYechildi?.Invoke(...) yozilgan. ?. operatori hodisa null bo'lsa chaqirmaydi.

Bu event bilan ishlashning standart shakli.

Amaliy misol: saralash mezoni #

CSHARP
(string Ism, int Yosh)[] odamlar =
[
    ("Husanboy", 25),
    ("Malika", 20),
    ("Nodira", 40)
];

Korsat("Ism bo'yicha", odamlar, o => o.Ism);
Korsat("Yosh bo'yicha", odamlar, o => o.Yosh);

void Korsat<TKalit>(string sarlavha, (string Ism, int Yosh)[] manba,
                    Func<(string Ism, int Yosh), TKalit> kalit)
{
    Console.WriteLine(sarlavha + ":");
    foreach (var o in manba.OrderBy(kalit))
    {
        Console.WriteLine($"  {o.Ism,-10} {o.Yosh}");
    }
}
Natija
Ism bo'yicha:
  Husanboy   25
  Malika     20
  Nodira     40
Yosh bo'yicha:
  Malika     20
  Husanboy   25
  Nodira     40

Delegat va generik birgalikda juda moslashuvchan kod beradi.

Amaliy topshiriq
  1. Func<int, int> bilan kvadrat hisoblovchi lambda yozing.
  2. Action<string> bilan xabar chiqaruvchi lambda yozing.
  3. Func<int, string, bool> qaysi turlarni olishini ayting.
  4. Shartni parametr sifatida qabul qiluvchi Filtrla metodini yozing.
  5. Uni uchta turli shart bilan chaqiring.
  6. Yopilma bilan tashqi o'zgaruvchini ushlang va uni o'zgartiring.
  7. for siklidagi yopilma tuzog'ini takrorlang va tuzating.
  8. Metod guruhi bilan ForEach(Console.WriteLine) ni sinang.
  9. Bitta Action ga uchta metodni bog'lang va bittasini o'chiring.
  10. event yozing, ikkita obunachi qo'shing va ?.Invoke bilan chaqiring.

Xulosa #

  • Delegat - metodga havola; uni saqlash va uzatish mumkin.
  • Func qiymat qaytaradi (oxirgi tur - natija), Action qaytarmaydi.
  • Lambda - nomsiz metodning qisqa yozuvi: son => son * 2.
  • Metodni parametr sifatida uzatish - LINQ ning asosi.
  • Yopilma tashqi o'zgaruvchining o'zini ushlaydi, nusxasini emas.
  • for siklida yopilma tuzog'i bor - ichida yangi o'zgaruvchi yarating.
  • Delegatga bir nechta metod bog'lash mumkin (+=, -=).
  • event delegatni himoyalaydi; chaqirishda ?.Invoke() ishlating.

Keyingi bo'limda fayllar va JSON bilan ishlashni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.