16-bo‘lim
Delegatlar, lambda va hodisalar
Delegat nima, Func va Action, lambda ifodalar, yopilmalar, metodni parametr sifatida uzatish va hodisalar bilan ishlash.
Ushbu bo‘lim mundarijasi
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:
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");
49
7
[LOG] dastur ishga tushdi
| Tur | Qaytaradimi | Misol |
|---|---|---|
Action | Yo'q | Action<string> - satr oladi, hech narsa qaytarmaydi |
Func | Ha | Func<int, int> - int oladi, int qaytaradi |
Predicate<T> | bool | Func<T, bool> bilan bir xil |
Func da oxirgi tur - qaytish turiFunc<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:
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)}");
10, 10, 10
Uchala yozuv ham bir xil. Birinchisi eng qisqa va eng ko'p ishlatiladigani.
Metodni parametr sifatida uzatish #
Bu delegatlarning asosiy foydasi:
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;
}
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:
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)}");
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.
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()))}");
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:
string[] sozlar = ["olma", "behi", "uzum"];
// lambda bilan
sozlar.ToList().ForEach(s => Console.WriteLine(s));
Console.WriteLine("---");
// metod guruhi - qisqaroq
sozlar.ToList().ForEach(Console.WriteLine);
olma
behi
uzum
---
olma
behi
uzum
Bir nechta metodni bog'lash #
Delegatga bir nechta metod qo'shish mumkin - ular ketma-ket chaqiriladi:
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}");
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:
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);
}
}
Husanboy: 50000 so'm yechildi, qoldiq 950000
SMS: 50000 so'm yechildi
Jurnal: -50000
Husanboy: mablag' yetarli emas
event va oddiy delegat farqiMaydonni event deb belgilash ikki narsani taqiqlaydi:
- Tashqaridan butunlay almashtirish (
hisob.PulYechildi = ...) mumkin emas - faqat+=va-=; - Tashqaridan chaqirish mumkin emas - faqat sinf o'zi
Invokeqila oladi.
Bu muhim: aks holda boshqa kod obunachilarni tasodifan o'chirib yuborishi yoki hodisani soxta chaqirishi mumkin edi.
null ni tekshiringHech 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 #
(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}");
}
}
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.
Func<int, int>bilan kvadrat hisoblovchi lambda yozing.Action<string>bilan xabar chiqaruvchi lambda yozing.Func<int, string, bool>qaysi turlarni olishini ayting.- Shartni parametr sifatida qabul qiluvchi
Filtrlametodini yozing. - Uni uchta turli shart bilan chaqiring.
- Yopilma bilan tashqi o'zgaruvchini ushlang va uni o'zgartiring.
forsiklidagi yopilma tuzog'ini takrorlang va tuzating.- Metod guruhi bilan
ForEach(Console.WriteLine)ni sinang. - Bitta
Actionga uchta metodni bog'lang va bittasini o'chiring. eventyozing, ikkita obunachi qo'shing va?.Invokebilan chaqiring.
Xulosa #
- Delegat - metodga havola; uni saqlash va uzatish mumkin.
Funcqiymat qaytaradi (oxirgi tur - natija),Actionqaytarmaydi.- Lambda - nomsiz metodning qisqa yozuvi:
son => son * 2. - Metodni parametr sifatida uzatish - LINQ ning asosi.
- Yopilma tashqi o'zgaruvchining o'zini ushlaydi, nusxasini emas.
forsiklida yopilma tuzog'i bor - ichida yangi o'zgaruvchi yarating.- Delegatga bir nechta metod bog'lash mumkin (
+=,-=). eventdelegatni himoyalaydi; chaqirishda?.Invoke()ishlating.
Keyingi bo'limda fayllar va JSON bilan ishlashni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.