20-bo‘lim
Amaliy loyiha - talabalar jurnali
Barcha o'rganilganni bitta dasturda birlashtiramiz: model, ombor interfeysi, JSON saqlash, LINQ hisobotlar, istisnolar va testlar.
Ushbu bo‘lim mundarijasi
Oxirgi bo'lim. Yangi mavzu yo'q - o'rgangan hamma narsangizni bitta ishlaydigan dasturda birlashtiramiz.
Vazifa: talabalar jurnali. Dastur talabalar va baholarni saqlaydi, fayldan o'qiydi, hisobot chiqaradi va o'zini sinovdan o'tkazadi.
Loyiha tuzilmasi #
Haqiqiy loyihada har bir sinf o'z faylida turadi:
Jurnal/
Program.cs <- kirish nuqtasi
Modellar/
Talaba.cs
Baho.cs
Daraja.cs
Ombor/
IJurnalOmbori.cs
JsonOmbor.cs
Xizmatlar/
JurnalXizmati.cs
Jurnal.csproj
Papkalar nom fazolariga mos kelishi qabul qilingan an'ana. Bu darslikda misollar qisqa bo'lishi uchun hammasi bitta faylda ko'rsatiladi.
1-qadam. Ma'lumot modeli #
Ma'lumot tashiydigan turlar uchun record (11-bo'lim):
var t = new Talaba(1, "Husanboy Qodirov", "A");
var b = new Baho(1, "Matematika", 92);
Console.WriteLine(t);
Console.WriteLine(b);
Console.WriteLine($"Daraja: {DarajaAniqla(92)}");
Console.WriteLine($"Daraja: {DarajaAniqla(68.5)}");
Daraja DarajaAniqla(double ball) => ball switch
{
>= 90 => Daraja.Alo,
>= 75 => Daraja.Yaxshi,
>= 60 => Daraja.Qoniqarli,
_ => Daraja.Qoniqarsiz
};
record Talaba(int Id, string Ism, string Guruh);
record Baho(int TalabaId, string Fan, int Ball);
enum Daraja { Qoniqarsiz, Qoniqarli, Yaxshi, Alo }
Talaba { Id = 1, Ism = Husanboy Qodirov, Guruh = A }
Baho { TalabaId = 1, Fan = Matematika, Ball = 92 }
Daraja: Alo
Daraja: Qoniqarli
record bepul ToString() berdi, switch ifodasi esa darajani
aniqladi (5-bo'lim).
2-qadam. Ombor interfeysi #
Ma'lumotni qayerda saqlashni interfeys orqali ajratamiz (10-bo'lim). Shunda keyin JSON o'rniga bazaga o'tish oson bo'ladi:
IJurnalOmbori ombor = new XotiraOmbori();
ombor.TalabaQosh(new Talaba(1, "Husanboy", "A"));
ombor.TalabaQosh(new Talaba(2, "Malika", "B"));
ombor.BahoQosh(new Baho(1, "Matematika", 92));
Console.WriteLine($"Talabalar: {ombor.Talabalar().Count}");
Console.WriteLine($"Baholar: {ombor.Baholar().Count}");
interface IJurnalOmbori
{
void TalabaQosh(Talaba talaba);
void BahoQosh(Baho baho);
IReadOnlyList<Talaba> Talabalar();
IReadOnlyList<Baho> Baholar();
}
class XotiraOmbori : IJurnalOmbori
{
private readonly List<Talaba> talabalar = [];
private readonly List<Baho> baholar = [];
public void TalabaQosh(Talaba talaba)
{
if (talabalar.Any(t => t.Id == talaba.Id))
{
throw new ArgumentException(
$"{talaba.Id} raqamli talaba allaqachon bor");
}
talabalar.Add(talaba);
}
public void BahoQosh(Baho baho) => baholar.Add(baho);
public IReadOnlyList<Talaba> Talabalar() => talabalar;
public IReadOnlyList<Baho> Baholar() => baholar;
}
record Talaba(int Id, string Ism, string Guruh);
record Baho(int TalabaId, string Fan, int Ball);
Talabalar: 2
Baholar: 1
IReadOnlyList nega ishlatildi?Agar List<Talaba> qaytarsak, chaqiruvchi kod unga to'g'ridan-to'g'ri
element qo'shishi mumkin - ya'ni TalabaQosh dagi tekshiruvni
chetlab o'tadi.
IReadOnlyList<T> esa faqat o'qishga ruxsat beradi. Ma'lumot bir xil,
lekin uni o'zgartirish yo'li faqat sinf metodlari orqali qoladi.
Bu 9-bo'limdagi inkapsulyatsiya qoidasining amaliy ko'rinishi.
3-qadam. Hisobot xizmati #
Hisoblashlar alohida sinfda - LINQ bilan (15-bo'lim):
(int Id, string Ism, string Guruh)[] talabalar =
[
(1, "Husanboy Qodirov", "A"),
(2, "Malika Tursunova", "B"),
(3, "Nodira Saidova", "A"),
(4, "Aziza Rahimova", "B")
];
(int TalabaId, string Fan, int Ball)[] baholar =
[
(1, "Matematika", 92), (1, "Fizika", 85),
(2, "Matematika", 78), (2, "Fizika", 88),
(3, "Matematika", 65), (3, "Fizika", 72),
(4, "Matematika", 95), (4, "Fizika", 91)
];
Shu ma'lumot bilan ishlaymiz. Bu yerda record o'rniga kortej
ishlatildi - hisobot misollarini qisqa qilish uchun. To'liq dasturda
(7-qadam) yana record ga qaytamiz.
Har bir talabaning o'rtachasi #
using System.Globalization;
var natijalar = talabalar
.Select(t => new
{
t.Ism,
t.Guruh,
Ortacha = baholar
.Where(b => b.TalabaId == t.Id)
.Average(b => b.Ball)
})
.OrderByDescending(x => x.Ortacha);
foreach (var n in natijalar)
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
"{0,-18} {1} {2,5:F1}", n.Ism, n.Guruh, n.Ortacha));
}
Aziza Rahimova B 93.0
Husanboy Qodirov A 88.5
Malika Tursunova B 83.0
Nodira Saidova A 68.5
Select ichida nomsiz tur yaratildi - bu vaqtinchalik natijalar uchun
qulay.
Guruhlar bo'yicha #
using System.Globalization;
var guruhlar = talabalar
.GroupBy(t => t.Guruh)
.Select(g => new
{
Guruh = g.Key,
Soni = g.Count(),
Ortacha = g.Average(t => baholar
.Where(b => b.TalabaId == t.Id)
.Average(b => b.Ball))
})
.OrderBy(x => x.Guruh);
foreach (var g in guruhlar)
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
"{0} guruhi: {1} talaba, o'rtacha {2:F1}",
g.Guruh, g.Soni, g.Ortacha));
}
A guruhi: 2 talaba, o'rtacha 78.5
B guruhi: 2 talaba, o'rtacha 88.0
Fanlar bo'yicha #
using System.Globalization;
var fanlar = baholar
.GroupBy(b => b.Fan)
.Select(g => new
{
Fan = g.Key,
Ortacha = g.Average(b => b.Ball),
Eng = g.Max(b => b.Ball)
})
.OrderByDescending(x => x.Ortacha);
foreach (var f in fanlar)
{
Console.WriteLine(string.Format(CultureInfo.InvariantCulture,
"{0,-12} o'rtacha {1:F1}, eng yuqori {2}", f.Fan, f.Ortacha, f.Eng));
}
Fizika o'rtacha 84.0, eng yuqori 91
Matematika o'rtacha 82.5, eng yuqori 95
4-qadam. JSON ga saqlash #
Ombor interfeysining fayl bilan ishlaydigan varianti (17-bo'lim):
using System.Text.Json;
string yol = Path.Combine(Path.GetTempPath(), "jurnal-sinov.json");
var ombor = new JsonOmbor(yol);
ombor.TalabaQosh(new Talaba(1, "Husanboy", "A"));
ombor.TalabaQosh(new Talaba(2, "Malika", "B"));
ombor.Saqla();
var ikkinchi = new JsonOmbor(yol);
ikkinchi.Yukla();
Console.WriteLine($"Fayldan o'qildi: {ikkinchi.Talabalar().Count} ta talaba");
foreach (var t in ikkinchi.Talabalar())
{
Console.WriteLine($" {t.Id}. {t.Ism} ({t.Guruh})");
}
File.Delete(yol);
class JsonOmbor(string yol)
{
private List<Talaba> talabalar = [];
public void TalabaQosh(Talaba talaba) => talabalar.Add(talaba);
public IReadOnlyList<Talaba> Talabalar() => talabalar;
public void Saqla() =>
File.WriteAllText(yol, JsonSerializer.Serialize(talabalar));
public void Yukla()
{
if (!File.Exists(yol))
{
talabalar = [];
return;
}
string json = File.ReadAllText(yol);
talabalar = JsonSerializer.Deserialize<List<Talaba>>(json) ?? [];
}
}
record Talaba(int Id, string Ism, string Guruh);
Fayldan o'qildi: 2 ta talaba
1. Husanboy (A)
2. Malika (B)
Yukla da fayl yo'qligini tekshirishDiqqat qiling: Yukla fayl mavjudligini tekshiradi va yo'q bo'lsa
bo'sh ro'yxat qo'yadi.
Bu 12-bo'limdagi qoidaning qo'llanishi: null emas, bo'sh to'plam
qaytaring. Dastur birinchi marta ishga tushganda fayl hali yo'q -
bu xato emas, odatiy holat.
?? [] qismi ham shu maqsadda: Deserialize null qaytarishi
mumkin (masalan fayl ichida null so'zi bo'lsa).
5-qadam. Xatolarni boshqarish #
var ombor = new Ombor();
Qosh(ombor, new Talaba(1, "Husanboy", "A"));
Qosh(ombor, new Talaba(1, "Boshqa odam", "B"));
Qosh(ombor, new Talaba(2, "", "A"));
Qosh(ombor, new Talaba(3, "Malika", "B"));
Console.WriteLine($"\nNatijada {ombor.Soni} ta talaba qo'shildi");
void Qosh(Ombor o, Talaba t)
{
try
{
o.Qosh(t);
Console.WriteLine($"OK: {t.Id} - {t.Ism}");
}
catch (ArgumentException x)
{
Console.WriteLine($"XATO: {x.Message}");
}
}
class Ombor
{
private readonly List<Talaba> talabalar = [];
public int Soni => talabalar.Count;
public void Qosh(Talaba talaba)
{
ArgumentException.ThrowIfNullOrWhiteSpace(talaba.Ism, nameof(talaba));
if (talabalar.Any(t => t.Id == talaba.Id))
{
throw new ArgumentException($"{talaba.Id} raqami band");
}
talabalar.Add(talaba);
}
}
record Talaba(int Id, string Ism, string Guruh);
OK: 1 - Husanboy
XATO: 1 raqami band
XATO: The value cannot be an empty string or composed entirely of whitespace. (Parameter 'talaba')
OK: 3 - Malika
Natijada 2 ta talaba qo'shildi
Xato bo'lgan yozuvlar o'tkazib yuborildi, dastur esa to'xtamadi (13-bo'lim).
6-qadam. Testlar #
Endi mantiqni sinovdan o'tkazamiz (19-bo'lim):
Tekshir.Teng(Daraja.Alo, DarajaAniqla(95), "95 ball");
Tekshir.Teng(Daraja.Alo, DarajaAniqla(90), "aynan 90 - chegara");
Tekshir.Teng(Daraja.Yaxshi, DarajaAniqla(89.9), "chegaradan bir oz past");
Tekshir.Teng(Daraja.Yaxshi, DarajaAniqla(75), "aynan 75");
Tekshir.Teng(Daraja.Qoniqarli, DarajaAniqla(60), "aynan 60");
Tekshir.Teng(Daraja.Qoniqarsiz, DarajaAniqla(59.9), "60 dan past");
Tekshir.Teng(Daraja.Qoniqarsiz, DarajaAniqla(0), "nol ball");
Tekshir.Yakun();
Daraja DarajaAniqla(double ball) => ball switch
{
>= 90 => Daraja.Alo,
>= 75 => Daraja.Yaxshi,
>= 60 => Daraja.Qoniqarli,
_ => Daraja.Qoniqarsiz
};
enum Daraja { Qoniqarsiz, Qoniqarli, Yaxshi, Alo }
static class Tekshir
{
private static int otdi, yiqildi;
public static void Teng<T>(T kutilgan, T haqiqiy, string nom)
{
if (Equals(kutilgan, haqiqiy))
{
otdi++;
Console.WriteLine($" OK {nom}");
}
else
{
yiqildi++;
Console.WriteLine($" XATO {nom}: {kutilgan} != {haqiqiy}");
}
}
public static void Yakun() =>
Console.WriteLine($"\nO'tdi: {otdi}, yiqildi: {yiqildi}");
}
OK 95 ball
OK aynan 90 - chegara
OK chegaradan bir oz past
OK aynan 75
OK aynan 60
OK 60 dan past
OK nol ball
O'tdi: 7, yiqildi: 0
Diqqat: testlarning yarmi chegaraviy qiymatlar - aynan 90, 75, 60 va ulardan bir oz past. Xato odatda shu yerda bo'ladi.
7-qadam. To'liq dastur #
Hammasini birlashtiramiz:
using System.Globalization;
using System.Text.Json;
string yol = Path.Combine(Path.GetTempPath(), "jurnal-yakuniy.json");
var inv = CultureInfo.InvariantCulture;
// 1. Ma'lumot tayyorlash va saqlash
var jurnal = new Jurnal();
jurnal.TalabaQosh(new Talaba(1, "Husanboy Qodirov", "A"));
jurnal.TalabaQosh(new Talaba(2, "Malika Tursunova", "B"));
jurnal.TalabaQosh(new Talaba(3, "Nodira Saidova", "A"));
jurnal.TalabaQosh(new Talaba(4, "Aziza Rahimova", "B"));
int[,] ballar = { { 92, 85 }, { 78, 88 }, { 65, 72 }, { 95, 91 } };
for (int i = 0; i < 4; i++)
{
jurnal.BahoQosh(new Baho(i + 1, "Matematika", ballar[i, 0]));
jurnal.BahoQosh(new Baho(i + 1, "Fizika", ballar[i, 1]));
}
File.WriteAllText(yol, jurnal.Json());
// 2. Fayldan qayta o'qish
var oqilgan = Jurnal.JsonDan(File.ReadAllText(yol));
Console.WriteLine("=== TALABALAR JURNALI ===\n");
Console.WriteLine("Reyting:");
foreach (var n in oqilgan.Reyting())
{
Console.WriteLine(string.Format(inv, " {0,-18} {1} {2,5:F1} {3}",
n.Ism, n.Guruh, n.Ortacha, n.Daraja));
}
Console.WriteLine("\nGuruhlar:");
foreach (var g in oqilgan.GuruhlarBoyicha())
{
Console.WriteLine(string.Format(inv, " {0}: {1} talaba, o'rtacha {2:F1}",
g.Guruh, g.Soni, g.Ortacha));
}
Console.WriteLine(string.Format(inv,
"\nUmumiy o'rtacha: {0:F2}", oqilgan.UmumiyOrtacha()));
int alochilar = oqilgan.Reyting().Count(r => r.Daraja == Daraja.Alo);
Console.WriteLine($"A'lochilar soni: {alochilar}");
File.Delete(yol);
record Talaba(int Id, string Ism, string Guruh);
record Baho(int TalabaId, string Fan, int Ball);
record Yozuv(string Ism, string Guruh, double Ortacha, Daraja Daraja);
enum Daraja { Qoniqarsiz, Qoniqarli, Yaxshi, Alo }
record JurnalHolati(List<Talaba> Talabalar, List<Baho> Baholar);
class Jurnal
{
private readonly List<Talaba> talabalar = [];
private readonly List<Baho> baholar = [];
public void TalabaQosh(Talaba t)
{
ArgumentException.ThrowIfNullOrWhiteSpace(t.Ism, nameof(t));
if (talabalar.Any(x => x.Id == t.Id))
{
throw new ArgumentException($"{t.Id} raqami band");
}
talabalar.Add(t);
}
public void BahoQosh(Baho b) => baholar.Add(b);
public string Json() =>
JsonSerializer.Serialize(new JurnalHolati(talabalar, baholar));
public static Jurnal JsonDan(string json)
{
var holat = JsonSerializer.Deserialize<JurnalHolati>(json)
?? new JurnalHolati([], []);
var j = new Jurnal();
foreach (var t in holat.Talabalar) j.TalabaQosh(t);
foreach (var b in holat.Baholar) j.BahoQosh(b);
return j;
}
public List<Yozuv> Reyting() =>
talabalar
.Select(t =>
{
double ortacha = baholar
.Where(b => b.TalabaId == t.Id)
.Average(b => b.Ball);
return new Yozuv(t.Ism, t.Guruh, ortacha, Daraja(ortacha));
})
.OrderByDescending(y => y.Ortacha)
.ToList();
public List<(string Guruh, int Soni, double Ortacha)> GuruhlarBoyicha() =>
Reyting()
.GroupBy(y => y.Guruh)
.Select(g => (g.Key, g.Count(), g.Average(y => y.Ortacha)))
.OrderBy(x => x.Key)
.ToList();
public double UmumiyOrtacha() => baholar.Average(b => b.Ball);
private static Daraja Daraja(double ball) => ball switch
{
>= 90 => global::Daraja.Alo,
>= 75 => global::Daraja.Yaxshi,
>= 60 => global::Daraja.Qoniqarli,
_ => global::Daraja.Qoniqarsiz
};
}
=== TALABALAR JURNALI ===
Reyting:
Aziza Rahimova B 93.0 Alo
Husanboy Qodirov A 88.5 Yaxshi
Malika Tursunova B 83.0 Yaxshi
Nodira Saidova A 68.5 Qoniqarli
Guruhlar:
A: 2 talaba, o'rtacha 78.5
B: 2 talaba, o'rtacha 88.0
Umumiy o'rtacha: 83.25
A'lochilar soni: 1
global:: nima uchun kerak bo'ldi?Jurnal sinfida Daraja degan metod bor va Daraja degan
enum ham bor. Sinf ichida Daraja.Alo deb yozsangiz, kompilyator
metodni ko'radi va xato beradi.
global::Daraja - "eng yuqori darajadagi Daraja ni nazarda
tutyapman" degani.
Amalda bunday to'qnashuvdan qochish kerak: metodni
DarajaAniqla deb nomlansa, muammo umuman bo'lmasdi. Bu yerda u
ataylab qoldirildi, chunki haqiqiy loyihada shunday holat uchraydi va
yechimini bilish foydali.
Nimalar ishlatildi #
| Bo'lim | Loyihada qayerda |
|---|---|
| 3 | decimal va double tanlovi, InvariantCulture |
| 5 | switch ifodasi bilan daraja aniqlash |
| 7 | List<T>, Dictionary |
| 9 | Inkapsulyatsiya, IReadOnlyList |
| 10 | IJurnalOmbori interfeysi |
| 11 | record va enum |
| 12 | ?? [] va bo'sh to'plam qaytarish |
| 13 | ArgumentException, try/catch |
| 15 | Select, GroupBy, OrderBy, Average |
| 17 | JSON va fayl |
| 19 | Chegaraviy testlar |
Keyingi qadamlar #
Eng yaxshi mashq - shu dasturga yangi imkoniyat qo'shish:
- Talabani o'chirish va tahrirlash;
- Chorak tushunchasi - baholarni vaqt bo'yicha ajratish;
- Konsol menyusi -
while (true)vaswitchbilan; - CSV ga eksport - Excel'da ochish uchun;
IJurnalOmborining SQLite varianti - bu saytdagi SQL va MySQL darsligi bilan birga.
Har bir qo'shimcha uchun avval test yozing, keyin kodni.
C# asoslari tugadi. Yo'nalishlar:
- Veb - ASP.NET Core; bu saytdagi HTML va CSS hamda JavaScript darsliklari bilan birga to'liq veb dasturchi bo'lasiz;
- Ma'lumotlar bazasi - Entity Framework Core; avval SQL va MySQL darsligini o'ting;
- O'yin - Unity; C# bilimingiz to'g'ridan-to'g'ri ishlaydi;
- Arxitektura - Dizayn naqshlari va Dasturiy ta'minot muhandisligi darsliklari;
- Algoritmlar - Algoritmlar va ma'lumotlar tuzilmasi darsligi; suhbatlarda eng ko'p so'raladigan mavzu.
dotnet new console -o Jurnalbilan loyiha yarating.Talaba,Bahorecord'lari vaDarajaenum'ini yozing.IJurnalOmboriinterfeysini va uning xotira variantini yozing.- Bir xil
Idqo'shishga urinishda istisno tashlang. - LINQ bilan reyting hisobotini chiqaring.
- Guruhlar va fanlar bo'yicha hisobot qo'shing.
- Ma'lumotni JSON ga saqlang va qayta o'qing.
DarajaAniqlauchun chegaraviy testlar yozing.- Barcha sonlarni
InvariantCulturebilan chiqaring. - Loyihaga menyu qo'shib, uni to'liq dasturga aylantiring.
Xulosa #
- Ma'lumot turlari uchun
record, mantiq uchunclass. - Saqlash usulini interfeys ortiga yashiring - keyin o'zgartirish oson.
- Tashqariga
IReadOnlyListqaytaring,Listemas. - Hisobotlarni LINQ bilan yozing - sikl deyarli kerak emas.
- Sonlarni
InvariantCulturebilan chiqaring. - Xatolarni kelib chiqish joyida tekshiring
(
ThrowIfNullOrWhiteSpace). - Fayl yo'q bo'lishi xato emas - bo'sh to'plam qaytaring.
- Testlarni chegaraviy qiymatlarga yo'naltiring.
Darslik tugadi. Endi sizda ishlaydigan C# dasturi va uni kengaytirish uchun yetarli bilim bor. Omad!
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.