14-bo‘lim
Generiklar
Generik metod va sinflar, tur parametrlari, cheklovlar, generik interfeyslar va nima uchun object o'rniga generik ishlatiladi.
Ushbu bo‘lim mundarijasi
Generik - tur parametri bilan yoziladigan kod. List<string> va
List<int> - bitta sinf, ikki xil tur bilan.
Bu yozilgan kodni qayta ishlatishning eng kuchli usullaridan biri.
Muammo: bir xil kod, turli turlar #
Ikkita sonni almashtiruvchi metod yozdingiz. Endi satrlar uchun ham
kerak. Keyin double uchun...
int a = 1, b = 2;
Almashtir(ref a, ref b);
Console.WriteLine($"int: {a}, {b}");
string x = "bir", y = "ikki";
Almashtir(ref x, ref y);
Console.WriteLine($"string: {x}, {y}");
double p = 1.5, q = 2.5;
Almashtir(ref p, ref q);
Console.WriteLine($"double: {p}, {q}");
void Almashtir<T>(ref T birinchi, ref T ikkinchi)
{
(birinchi, ikkinchi) = (ikkinchi, birinchi);
}
int: 2, 1
string: ikki, bir
double: 2.5, 1.5
Bitta metod uchala tur uchun ham ishladi. T - tur parametri,
chaqirishda uni kompilyator o'zi aniqlaydi.
object ishlatmaslik kerakGeneriklardan oldin hamma narsa object orqali qilinardi. Uch
muammosi bor:
- Turlar tekshirilmaydi.
objectro'yxatiga tasodifan satr ham, son ham tushadi va kompilyator ogohlantirmaydi. - Qaytarib aylantirish kerak. Har safar
(int)yozish - qo'shimcha kod va potensial istisno. - Qiymat turlari qutilanadi.
intniobjectga aylantirish uni uyumga ko'chiradi - bu xotira va vaqt sarfi.
Generiklar uchala muammoni ham hal qiladi.
Generik sinf #
var sonlar = new Quti<int>();
sonlar.Qoy(42);
var matnlar = new Quti<string>();
matnlar.Qoy("Namangan");
Console.WriteLine(sonlar.Ol());
Console.WriteLine(matnlar.Ol());
Console.WriteLine($"Turlari: {sonlar.TurNomi} va {matnlar.TurNomi}");
class Quti<T>
{
private T? ichidagi;
public void Qoy(T narsa) => ichidagi = narsa;
public T? Ol() => ichidagi;
public string TurNomi => typeof(T).Name;
}
42
Namangan
Turlari: Int32 va String
Cheklovlar #
Ba'zan T haqida biror narsa bilish kerak bo'ladi. Cheklovsiz T
ustida deyarli hech narsa qilib bo'lmaydi:
Console.WriteLine(Kattasi(5, 9));
Console.WriteLine(Kattasi("olma", "behi"));
Console.WriteLine(Kattasi(2.5, 1.5));
T Kattasi<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) >= 0 ? a : b;
}
9
olma
2.5
where T : IComparable<T> - "T solishtirilishi mumkin bo'lsin"
degani. Shu tufayli CompareTo ni chaqira oldik.
| Cheklov | Ma'nosi |
|---|---|
where T : class | Havola turi bo'lsin |
where T : struct | Qiymat turi bo'lsin |
where T : new() | Parametrsiz konstruktori bo'lsin |
where T : Shakl | Shu sinfdan meros olgan bo'lsin |
where T : IComparable<T> | Shu interfeysni amalga oshirgan bo'lsin |
where T : notnull | null bo'lmasin |
Bir nechta cheklovni birga yozish ham mumkin:
var omborxona = new Ombor<Mahsulot>();
omborxona.Yarat();
omborxona.Yarat();
Console.WriteLine($"Yaratildi: {omborxona.Soni} ta");
Console.WriteLine(omborxona.Birinchi());
class Ombor<T> where T : class, new()
{
private readonly List<T> narsalar = [];
public void Yarat() => narsalar.Add(new T());
public int Soni => narsalar.Count;
public T? Birinchi() => narsalar.Count > 0 ? narsalar[0] : null;
}
class Mahsulot
{
public override string ToString() => "Yangi mahsulot";
}
Yaratildi: 2 ta
Yangi mahsulot
new() cheklovi tufayli sinf ichida new T() yozish mumkin bo'ldi.
Bir nechta tur parametri #
var juft = new Juftlik<string, int>("Namangan", 626000);
Console.WriteLine(juft);
var boshqa = new Juftlik<int, bool>(42, true);
Console.WriteLine(boshqa);
class Juftlik<TBirinchi, TIkkinchi>(TBirinchi birinchi, TIkkinchi ikkinchi)
{
public TBirinchi Birinchi => birinchi;
public TIkkinchi Ikkinchi => ikkinchi;
public override string ToString() => $"({Birinchi}, {Ikkinchi})";
}
(Namangan, 626000)
(42, True)
Dictionary<TKey, TValue> aynan shu tarzda qurilgan.
An'ana: bitta bo'lsa T, bir nechta bo'lsa ma'noli nom bilan va
T harfidan boshlab: TKey, TValue, TNatija.
Bu kod o'qiyotgan odamga darhol "bu tur parametri, oddiy sinf emas" deb bildiradi.
Generik interfeys #
var sonOmbor = new XotiraOmbori<int>();
sonOmbor.Saqla(1, 100);
sonOmbor.Saqla(2, 200);
Console.WriteLine(sonOmbor.Ol(1));
Console.WriteLine($"Yo'q kalit: {sonOmbor.Ol(99)}");
Console.WriteLine($"Jami: {sonOmbor.Soni}");
interface IOmbor<T>
{
void Saqla(int kalit, T qiymat);
T? Ol(int kalit);
int Soni { get; }
}
class XotiraOmbori<T> : IOmbor<T>
{
private readonly Dictionary<int, T> malumot = [];
public void Saqla(int kalit, T qiymat) => malumot[kalit] = qiymat;
public T? Ol(int kalit) =>
malumot.TryGetValue(kalit, out T? q) ? q : default;
public int Soni => malumot.Count;
}
100
Yo'q kalit: 0
Jami: 2
T? - bu Nullable<T> emasYuqoridagi chiqishga diqqat qiling: mavjud bo'lmagan kalit uchun
0 qaytdi, null emas.
Sababi nozik: cheklovsiz generikda T? Nullable<T> degani emas.
U shunchaki "bu qiymat default bo'lishi mumkin" degan izoh. T ning
o'rniga int qo'yilganda T? oddiy int bo'lib qoladi - va int
hech qachon null bo'la olmaydi.
Shuning uchun quyidagi kod kompilyatsiya qilinmaydi:
error CS0019: Operator '??' cannot be applied to operands
of type 'int' and 'int'
Agar null qaytarish kerak bo'lsa, tur parametrini cheklang:
| Cheklov | T? nimani bildiradi |
|---|---|
| cheklovsiz | default(T) bo'lishi mumkin - int uchun 0 |
where T : class | Haqiqiy null |
where T : struct | Haqiqiy Nullable<T>, ya'ni int? |
Bu C# dagi eng ko'p chalkashtiriladigan joylardan biri - jadvalni yodda tuting.
default nima qaytaradidefault - turning sukut qiymati:
| Tur | default |
|---|---|
int, double | 0 |
bool | false |
string va boshqa havola turlari | null |
struct | Barcha maydonlari sukut qiymatda |
Generik kodda null deb yozib bo'lmaydi (chunki T qiymat turi
bo'lishi mumkin), shuning uchun default ishlatiladi.
Generik metod va tur aniqlash #
Console.WriteLine(Birlashtir(1, 2, 3));
Console.WriteLine(Birlashtir("a", "b"));
Console.WriteLine(Birlashtir<double>(1.5, 2.5));
string Birlashtir<T>(params T[] narsalar) => string.Join(" + ", narsalar);
1 + 2 + 3
a + b
1.5 + 2.5
Ko'p hollarda <T> ni yozish shart emas - kompilyator argumentlardan
aniqlaydi. Oshkora yozish faqat noaniqlik bo'lganda kerak.
Amaliy misol: natija turi #
Generiklar bilan foydali naqsh - "muvaffaqiyat yoki xato" turini yaratish:
Natija<int> yaxshi = Bolish(10, 2);
Natija<int> yomon = Bolish(10, 0);
Console.WriteLine(yaxshi.Xabar());
Console.WriteLine(yomon.Xabar());
Natija<int> Bolish(int a, int b) =>
b == 0
? Natija<int>.Xato("nolga bo'lib bo'lmaydi")
: Natija<int>.Muvaffaqiyat(a / b);
class Natija<T>
{
private readonly T? qiymat;
private readonly string? xato;
private Natija(T? qiymat, string? xato)
{
this.qiymat = qiymat;
this.xato = xato;
}
public static Natija<T> Muvaffaqiyat(T qiymat) => new(qiymat, null);
public static Natija<T> Xato(string sabab) => new(default, sabab);
public string Xabar() =>
xato is null ? $"Natija: {qiymat}" : $"Xato: {xato}";
}
Natija: 5
Xato: nolga bo'lib bo'lmaydi
Bu yondashuv istisno tashlashning muqobili: xato qaytarish qiymatining bir qismi bo'ladi va uni e'tiborsiz qoldirib bo'lmaydi.
- Ikki qiymatni almashtiruvchi generik metod yozing.
- Uni uchta turli tur bilan chaqiring.
- Generik
Quti<T>sinfini yarating. where T : IComparable<T>cheklovi bilan kattasini topuvchi metod yozing.- Cheklovni olib tashlab, kompilyator nima deyishini ko'ring.
where T : new()bilan obyekt yaratuvchi sinf yozing.- Ikki tur parametrli
Juftliksinfini yozing. - Generik interfeys e'lon qilib, uni amalga oshiring.
defaultqaysi turlarda nima qaytarishini tekshiring.objectva generik yondashuv farqini o'z so'zingiz bilan yozing.
Xulosa #
- Generik - tur parametri bilan yoziladigan kod; bitta metod ko'p tur uchun ishlaydi.
objecto'rniga generik: turlar tekshiriladi, aylantirish kerak emas, qutilash yo'q.- Cheklovlar (
where)Thaqida nima bilishni belgilaydi. where T : new()- sinf ichidanew T()yozishga ruxsat beradi.- Bir nechta tur parametri
TKey,TValuekabi nomlanadi. - Generik kodda
nullemas,defaultishlatiladi. - Ko'pincha
<T>ni yozish shart emas - kompilyator o'zi aniqlaydi.
Keyingi bo'limda LINQ ni - C# ning eng kuchli vositalaridan birini 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.