Grafikus C# – Windows Forms tanfolyam › 13.10 lecke Windows Forms · 13.10 lecke · záró projekt A 13. fejezet záró projektjében mások kódját kell kivizsgálnod és megjavítanod, mint egy valódi karbantartónak. Kapsz egy diáknyilvántartót, amely lefordul-e egyáltalán, kérdéses; ha igen, akkor összeomlik, rossz eredményt ad, szivárog, és nem vezet naplót. A feladatod nem egyszerűen „kijavítani”, hanem módszeresen dolgozni: reprodukálni, a megfelelő eszközzel megtalálni az okot (Error List, debugger, napló), egyenként javítani, tesztet írni, és mindent dokumentálni. A kész munka egy hibajegy-lista, egy tesztterv, a javított program és egy rövid összegzés.Mini projekt: hibás alkalmazás javítása

A projekt végére
- kivizsgálsz egy idegen, hibás programot a megfelelő eszközökkel;
- javítod a fordítási, logikai és futási hibákat, és a hiányzó védelmeket (bevitel, fájl);
- naplózást adsz a programhoz, és kezeled a váratlan hibákat;
- tesztterv és hibajegyek segítségével dokumentálsz;
- megtalálod és megszünteted az erőforrás-szivárgást;
- önértékeled, mit tanultál a fejezetből.
Mielőtt elkezded
Ehhez a projekthez a 13.1–13.9 lecke szükséges.
Követelménytábla (100 pont)
| Követelmény | Pont |
|---|---|
| Fordítási hibák: mind a 4 fordítási hiba megtalálva és javítva (Error List, hibakód naplóban); a projekt figyelmeztetésmentesen fordul. | 8 |
| Logikai hibák: a hibás átlag, a rossz „legjobb tanuló”, a jegyhatárok javítva; a hibák debuggerrel megtalálva (töréspont, Watch, Call Stack), a naplóban hipotézissel és bizonyítékkal. | 15 |
| Futási hibák: az összeomlások (null lista, üres átlag, nincs kijelölés) kivételkezelés és ellenőrzés mellett javítva; a globális hibakezelő beállítva. | 15 |
Bevitel-ellenőrzés: név (kötelező, hossz, duplikátum), jegy (egész, 1–5), kijelölés; TryParse, ErrorProvider; a határértékek tesztelve. | 12 |
| Fájlkezelés: biztonságos mentés és betöltés (felhasználói mappa, ideiglenes fájl, kivételek érthető üzenettel, sérült fájl kezelése, CSV-mezők védelme). | 15 |
Naplózás: INaplo/fájlnapló, szintek, indulási adatok, hibák ex.ToString()-gal, nincs érzékeny adat. | 10 |
| Erőforrás-kezelés: a rajzolóobjektum-szivárgás és az esemény-szivárgás megszüntetve; a mérés (előtte–utána) dokumentálva. | 5 |
| Tesztelés: tesztterv (legalább 20 teszteset) és legalább 10 hibajegy a mintaformátum szerint; a regressziós futtatás igazolva. | 15 |
| Dokumentáció és önértékelés: összegzés: mi volt a hiba, mivel találtad meg, hogyan javítottad, mit tanultál. | 5 |
A feladat és a hibajegy-sablon
A program a Diáknyilvántartó: diákokat vesz fel, jegyeket (1–5) rögzít hozzájuk, átlagot és a legjobb tanulót számolja, CSV-be ment és onnan tölt be. A működés specifikációja (ehhez mérd a hibákat):
- Új diák: a név kötelező (2–40 karakter), egyedi (kis-nagybetű nélkül).
- Jegy: egész szám 1–5; csak kijelölt diákhoz adható.
- A lista minden sora: Név (átlag: 4,33); jegy nélküli diáknál (nincs jegy).
- Az átlag valós szám, két tizedesre kerekítve.
- Legjobb tanuló: a legmagasabb átlagú diák (a jegy nélküliek kimaradnak); döntetlennél az első betűrendben.
- Mentés/betöltés: UTF-8 CSV a felhasználói mappába; a hibákról érthető üzenet; a betöltés a sérült sorokat kihagyja és jelzi.
- A program a hibákat naplózza; kezeletlen kivételtől nem omlik össze.
BUG-xx: [egy mondatos cím: mi, hol]
Környezet: verzió, Windows, kultúra (hu-HU), felbontás
Lépések: 1. ... 2. ... 3. ...
Elvárt: ... (a specifikációból)
Tényleges: ... (hibaüzenet / eredmény)
Hogyan találtad: Error List / debugger (töréspont, Watch) / napló / teszt / Feladatkezelő
Ok: [a kódban: melyik sor és miért]
Javítás: [mit változtattál]
Súlyosság: kritikus / súlyos / közepes / kicsi
Regresszió: [melyik tesztesetet futtattad a javítás után]Mit tegyél ide: a diáknyilvántartó felülete (lista, mezők, gombok) és az első hibaüzenet.Javasolt fájlnév: winforms-13-10-felulet.webp · Alt szöveg: A diáknyilvántartó felülete
A 8 lépés támponttal
Haladj lépésről lépésre, és minden lépést jegyezz le (hibajegyként). Minden javítás után futtasd a programot. A támpont segít elindulni, de a megoldást magad találd meg. Hozd létre a Formot a leírt vezérlőkkel ( A legelső hibával kezdd; fordíts újra minden javítás után. Jegyezd fel a kódot, a sort és az okot. A figyelmeztetéseket is takarítsd el. Támpont: 13.1 lecke. Adj a programhoz Reprodukáld az összeomlásokat (új diák felvétele, jegy kijelölés nélkül, átlag jegy nélkül). A debugger Exception Helper és a Call Stack megmutatja az okot. Javítsd ellenőrzéssel (megelőzés), a környezeti hibákat A program „jól fut”, de rossz az eredmény: az átlag, a legjobb tanuló, a hibás jegy. Használj töréspontot, Locals/Watch ablakot és Immediate ablakot. Először írd le, mit vársz (a specifikációból), aztán nézd meg, mit lát a debugger (13.2–13.3 lecke). Központi Biztonságos mentés a felhasználói mappába ( Mérd a program memóriáját és GDI-objektumait hosszabb futás közben (Feladatkezelő): a 1. lépés: felépítés és az első fordítás
txtNev, txtJegy, btnHozzaad, btnJegy, btnLegjobb, btnMent, btnBetolt, btnTorol, lstDiakok, lblLegjobb, tmr (Timer, 500 ms), pnlJelzo (Panel), tsslUzenet), másold be a hibás kódot, és fordítsd le. Az Error List mutatja a fordítási hibákat. Támpont: 13.1 lecke.2. lépés: fordítási hibák (Error List)
3. lépés: naplózás bevezetése
INaplo és FajlNaplo megoldást (13.7 lecke): indulási adatok, műveletek, hibák. A többi lépés előtt tedd be, hogy a hibákat már a naplóban is lásd. Ne naplózz személyes adatot (a diák neve helyett azonosítót vagy hosszat).4. lépés: futási hibák és globális kezelő
try–catch-csel. Állítsd be a globális hibakezelőt a Program.cs-ben (13.4 lecke).5. lépés: logikai hibák (debugger)
6. lépés: bevitel-ellenőrzés
Ellenorzo osztály: név, jegy; TryParse, ErrorProvider; határértékek (0, 1, 5, 6, üres, szóköz, hosszú, ékezetes). Támpont: 13.5 lecke.7. lépés: fájlkezelés
ApplicationData), ideiglenes fájllal; a CSV-mezők védelme (a név tartalmazhat ; és " jelet: idézés); a betöltésnél a sérült sor kihagyása és jelzése; hibák érthető üzenettel. Támpont: 13.6 lecke és 10.5 lecke.8. lépés: szivárgás, teszt, összegzés
Timer-es rajzolás szivárog (Pen/Font felszabadítás nélkül). Javítsd és mérd újra. Írj tesztervet (20 teszteset) és futtasd a regressziós készletet. Végül írd meg az összegzést (mit találtál, mivel, mit tanultál). Támpont: 13.8–13.9 lecke.
A hibás program, a hibakatalógus és a mintamegoldás
Előbb dolgozz önállóan a hibás programmal; a katalógust és a javított kódot csak a vizsgálat után nyisd meg. Megjegyzések: a név a mintában tiltja a A hibás program (ezt másold a Form1.cs-be)
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace Diaknyilvantarto
{
public class Diak
{
public string Nev;
public List<int> Jegyek = new List<int>();
public double Atlag()
{
int osszeg = 0;
foreach (int j in Jegyek) { osszeg += j; }
return osszeg / Jegyek.Count;
}
}
public partial class Form1 : Form
{
private List<Diak> diakok;
public Form1()
{
InitializeComponent()
tmr.Start();
}
private void btnHozzaad_Click(object sender, EventArgs e)
{
diakok.Add(new Diak { Nev = txtNev.Text });
Frissit();
}
private void btnJegy_Click(object sender, EventArgs e)
{
int jegy = txtJegy.Text;
Diak d = diakok[lstDiakok.SelectedIndex];
d.Jegyek.Add(jegy);
Frissit();
}
private void btnLegjobb_Click(object sender, EventArgs e)
{
Diak legjobb = diakok.OrderBy(x => x.Atlag()).First();
lblLegjobb.Text = "Legjobb: " + legjobb.Nevv;
}
private void Frissit()
{
lstDiakok.Items.Clear();
foreach (Diak d in diakok) { lstDiakok.Items.Add(d.Nev + " (átlag: " + d.Atlag() + ")"); }
}
private void btnMent_Click(object sender, EventArgs e)
{
string s = "";
foreach (Diak d in diakok) { s += d.Nev + ";" + string.Join(",", d.Jegyek) + "\n"; }
File.WriteAllText(@"C:\diakok.csv", s);
}
private void btnBetolt_Click(object sender, EventArgs e)
{
diakok = new List<Diak>();
foreach (string sor in File.ReadAllLines(@"C:\diakok.csv"))
{
string[] r = sor.Split(';');
Diak d = new Diak { Nev = r[0] };
foreach (string j in r[1].Split(',')) { d.Jegyek.Add(int.Parse(j)); }
diakok.Add(d);
}
Frissit();
}
private void tmr_Tick(object sender, EventArgs e)
{
using (Graphics g = pnlJelzo.CreateGraphics())
{
Pen toll = new Pen(Color.Purple, 2);
Font betu = new Font("Arial", 10);
g.Clear(Color.White);
g.DrawRectangle(toll, 2, 2, 90, 22);
g.DrawString(DateTime.Now.ToString("HH:mm:ss"), betu, Brushes.Black, 6, 4);
}
}
}
}A hibakatalógus (a vizsgálat után nézd meg!)
Azonosító Hiba Fajta / lecke Eszköz C1 InitializeComponent() után hiányzik a ;fordítási (CS1002), 13.1 Error List C2 legjobb.Nevv elírásfordítási (CS1061), 13.1 Error List C3 int jegy = txtJegy.Text;fordítási (CS0029), 13.1 Error List C4 hiányzó using System.Linq; (OrderBy, First)fordítási (CS1061), 13.1 Ctrl+. L1 az átlag egész osztással számol ( osszeg / Count)logikai, 13.2–13.3 töréspont, Watch L2 a OrderBy növekvő: a legrosszabb tanulót adjalogikai, 13.3 Immediate, Locals R1 üres jegylista átlaga: DivideByZeroException; a lista frissítése is leállfutási, 13.4 Exception Helper R2 a diakok lista nincs létrehozva: NullReferenceExceptionfutási, 13.3–13.4 debugger, Locals R3 nincs kijelölés: SelectedIndex = -1 → ArgumentOutOfRangeExceptionfutási, 13.4 Exception Helper V1 a név nincs ellenőrizve (üres, szóköz, hosszú, duplikált, ;)bevitel, 13.5 határértékek V2 a jegy nincs ellenőrizve ( TryParse, 1–5)bevitel, 13.5 határértékek F1 mentés a C:\ gyökerébe: UnauthorizedAccessException; nincs tryfájl, 13.6 teszt, napló F2 a név ; vagy sortörés jele elrontja a CSV-t; a mentés nem biztonságos (csonka fájl)fájl, 13.6 teszt F3 a betöltés nem létező fájlra FileNotFoundExceptionfájl, 13.6 teszt F4–F5 sérült sor: IndexOutOfRange, üres jegylista: FormatExceptionfájl és bevitel, 13.5–13.6 sérült fájl teszt N1 nincs naplózás, nincs globális hibakezelő diagnosztika, 13.4, 13.7 áttekintés S1 a Timer minden 500 ms-ban új Pen és Font objektumot hoz létre felszabadítás nélkül: GDI-szivárgáserőforrás, 13.9 Feladatkezelő (GDI) A javított program (próbáld ki előbb önállóan!)
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Diaknyilvantarto
{
public static class Ellenorzo
{
public static string Nev(string s, IEnumerable<Diak> meglevok)
{
if (string.IsNullOrWhiteSpace(s)) { return "A név kötelező."; }
s = s.Trim();
if (s.Length < 2 || s.Length > 40) { return "A név 2–40 karakter lehet."; }
if (s.IndexOfAny(new char[] { ';', '\n', '\r' }) >= 0) { return "A név nem tartalmazhat pontosvesszőt vagy sortörést."; }
if (meglevok.Any(d => string.Equals(d.Nev, s, StringComparison.CurrentCultureIgnoreCase))) { return "Ilyen nevű diák már van."; }
return null;
}
public static string Jegy(string s, out int jegy)
{
jegy = 0;
if (string.IsNullOrWhiteSpace(s)) { return "A jegy kötelező."; }
if (!int.TryParse(s.Trim(), out jegy)) { return "A jegy egész szám kell legyen."; }
if (jegy < 1 || jegy > 5) { return "A jegy 1 és 5 között lehet."; }
return null;
}
}
public class Diak
{
public string Nev { get; private set; }
public List<int> Jegyek { get; private set; }
public Diak(string nev) { Nev = nev.Trim(); Jegyek = new List<int>(); }
public double? Atlag() // L1, R1: valós osztás; jegy nélkül nincs átlag (null)
{
if (Jegyek.Count == 0) { return null; }
return (double)Jegyek.Sum() / Jegyek.Count;
}
public override string ToString()
{
double? a = Atlag();
return Nev + (a.HasValue ? " (átlag: " + a.Value.ToString("0.00") + ")" : " (nincs jegy)");
}
}
public partial class Form1 : Form
{
private readonly List<Diak> diakok = new List<Diak>(); // R2
private readonly INaplo naplo;
private readonly Pen toll = new Pen(Color.Purple, 2); // S1: egyszer létrehozva
private readonly Font betu = new Font("Arial", 10);
private readonly string csvUt;
public Form1()
{
InitializeComponent(); // C1
string mappa = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Diaknyilvantarto");
csvUt = Path.Combine(mappa, "diakok.csv"); // F1: felhasználói mappa
naplo = new FajlNaplo(mappa);
naplo.Info("A program elindult. Verzió: " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version + "; kultúra: " + System.Globalization.CultureInfo.CurrentCulture.Name);
errorProvider1.BlinkStyle = ErrorBlinkStyle.NeverBlink;
tmr.Start();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
tmr.Stop();
toll.Dispose();
betu.Dispose(); // S1
base.OnFormClosed(e);
}
private void btnHozzaad_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
string hiba = Ellenorzo.Nev(txtNev.Text, diakok); // V1
if (hiba != null) { errorProvider1.SetError(txtNev, hiba); tsslUzenet.Text = hiba; return; }
diakok.Add(new Diak(txtNev.Text));
naplo.Info("Új diák felvéve (összesen: " + diakok.Count + ")"); // nincs név a naplóban
txtNev.Clear();
Frissit();
}
private void btnJegy_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
if (lstDiakok.SelectedIndex < 0) { tsslUzenet.Text = "Előbb jelölj ki egy diákot."; return; } // R3
int jegy;
string hiba = Ellenorzo.Jegy(txtJegy.Text, out jegy); // C3, V2
if (hiba != null) { errorProvider1.SetError(txtJegy, hiba); tsslUzenet.Text = hiba; return; }
int i = lstDiakok.SelectedIndex;
diakok[i].Jegyek.Add(jegy);
txtJegy.Clear();
Frissit();
lstDiakok.SelectedIndex = i;
}
private void btnLegjobb_Click(object sender, EventArgs e)
{
Diak legjobb = diakok.Where(x => x.Atlag().HasValue) // C4: using System.Linq; a jegy nélküliek kimaradnak
.OrderByDescending(x => x.Atlag().Value) // L2: csökkenő
.ThenBy(x => x.Nev, StringComparer.CurrentCultureIgnoreCase)
.FirstOrDefault();
lblLegjobb.Text = legjobb == null ? "Még nincs átlag." : "Legjobb: " + legjobb.Nev + " (" + legjobb.Atlag().Value.ToString("0.00") + ")"; // C2
}
private void Frissit()
{
lstDiakok.BeginUpdate();
lstDiakok.Items.Clear();
foreach (Diak d in diakok) { lstDiakok.Items.Add(d.ToString()); }
lstDiakok.EndUpdate();
}
private void btnMent_Click(object sender, EventArgs e)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(csvUt));
StringBuilder sb = new StringBuilder();
foreach (Diak d in diakok) { sb.Append(d.Nev).Append(';').Append(string.Join(",", d.Jegyek)).AppendLine(); }
string tmp = csvUt + ".tmp";
File.WriteAllText(tmp, sb.ToString(), new UTF8Encoding(false)); // F2: biztonságos mentés
if (File.Exists(csvUt)) { File.Replace(tmp, csvUt, csvUt + ".bak"); } else { File.Move(tmp, csvUt); }
tsslUzenet.Text = "Mentve (" + diakok.Count + " diák).";
naplo.Info("Mentés kész: " + diakok.Count + " diák");
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) // F1
{
tsslUzenet.Text = "A mentés nem sikerült: " + ex.Message;
naplo.Hiba("Mentési hiba", ex);
}
}
private void btnBetolt_Click(object sender, EventArgs e)
{
if (!File.Exists(csvUt)) { tsslUzenet.Text = "Még nincs mentett fájl."; return; } // F3 (előszűrés)
List<Diak> uj = new List<Diak>();
int hibas = 0;
try
{
foreach (string sor in File.ReadAllLines(csvUt, Encoding.UTF8))
{
if (string.IsNullOrWhiteSpace(sor)) { continue; }
string[] r = sor.Split(';'); // F4
if (r.Length != 2 || Ellenorzo.Nev(r[0], uj) != null) { hibas++; continue; }
Diak d = new Diak(r[0]);
bool jo = true;
foreach (string j in r[1].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) // F5
{
int jegy;
if (Ellenorzo.Jegy(j, out jegy) == null) { d.Jegyek.Add(jegy); } else { jo = false; }
}
if (jo) { uj.Add(d); } else { hibas++; }
}
}
catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) // F3
{
tsslUzenet.Text = "A betöltés nem sikerült: " + ex.Message;
naplo.Hiba("Betöltési hiba", ex);
return;
}
diakok.Clear();
diakok.AddRange(uj);
Frissit();
tsslUzenet.Text = "Betöltve: " + uj.Count + " diák" + (hibas > 0 ? ", " + hibas + " sérült sor kihagyva." : ".");
if (hibas > 0) { naplo.Figyelmeztet("Betöltés: " + hibas + " sérült sor kihagyva"); }
}
private void tmr_Tick(object sender, EventArgs e)
{
using (Graphics g = pnlJelzo.CreateGraphics()) // a Graphics-ot a using zárja
{
g.Clear(Color.White);
g.DrawRectangle(toll, 2, 2, 90, 22); // a Pen és Font mezőből (egyszer létrehozva)
g.DrawString(DateTime.Now.ToString("HH:mm:ss"), betu, Brushes.Black, 6, 4);
}
}
}
}
// ---- Program.cs: globális védőháló (N1) ----
static void Main()
{
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += (s, e) => { Naploz(e.Exception); MessageBox.Show("Váratlan hiba történt, a részleteket naplóztuk. Mentsd a munkádat, és indítsd újra a programot.", "Hiba"); };
AppDomain.CurrentDomain.UnhandledException += (s, e) => Naploz(e.ExceptionObject as Exception);
Application.EnableVisualStyles();
Application.Run(new Form1());
}; és a sortörés jelet (a CSV-t védi); a bővítés: idézőjeles mezők (10.5 lecke). A Naploz a 13.4 lecke védett naplózó metódusa. A Pen/Font mezőként él, és a Form zárásakor szabadul fel; a CreateGraphics() villogás-érzékeny, a valódi rajzolás helye a Paint esemény (9.3 lecke).
Tesztlista (18 pont)
A javított programon minden pontnál próbáld ki, és jegyezd fel az eredményt a tesztterv táblázatba (teszteset-azonosítóval).
| # | Teszt | Elvárt eredmény |
|---|---|---|
| 1 | A projekt lefordul | 0 hiba, 0 figyelmeztetés (Error List). |
| 2 | Új diák érvényes névvel | Felkerül a listára Név (nincs jegy) felirattal; a lista nem omlik össze. |
| 3 | Üres név, csak szóköz, 1 karakter, 41 karakter | Hibaüzenet és ErrorProvider jel; nem kerül a listára. |
| 4 | Név ; jellel, sortöréssel | Elutasítva (nem rontja el a CSV-t). |
| 5 | Azonos név másodszor (kis-nagybetű eltéréssel) | „Ilyen nevű diák már van.” |
| 6 | Jegy kijelölés nélkül | „Előbb jelölj ki egy diákot.”; nincs kivétel. |
| 7 | Jegy: 0, 1, 5, 6, üres, „abc”, „4,5” | 1 és 5 elfogadva; a többi hiba, érthető üzenettel. |
| 8 | Átlag: 5, 4 jegy után | 4,50 (nem 4); két tizedesre kerekítve. |
| 9 | Átlag jegy nélküli diáknál | (nincs jegy); nincs DivideByZeroException. |
| 10 | Legjobb tanuló több diákkal | A legmagasabb átlagú (nem a legalacsonyabb); a jegy nélküliek kimaradnak; döntetlennél betűrend. |
| 11 | Legjobb tanuló üres listánál | „Még nincs átlag.”; nincs kivétel. |
| 12 | Mentés, majd újraindítás és betöltés | Minden diák és jegy megvan; a fájl a felhasználói mappában van. |
| 13 | Mentés védett mappába (útvonal átírva) | Érthető hibaüzenet, a program fut tovább; a naplóban Error sor. |
| 14 | Betöltés nem létező fájlból | „Még nincs mentett fájl.”; nincs kivétel. |
| 15 | Betöltés kézzel elrontott CSV-ből (hiányzó ;, szöveg a jegyek közt) | A sérült sorok kihagyva, az üzenet megadja a számukat; a jó sorok betöltődnek. |
| 16 | Szándékos váratlan hiba (teszt-gomb) | A globális kezelő üzenetet ad, naplóz; a program nem omlik össze. |
| 17 | Napló ellenőrzése | Van naplófájl; indulási adatok, műveletek, a hibák ex.ToString()-gal; nincs diáknév benne. |
| 18 | Erőforrás-teszt: 10 perc futás | A Feladatkezelőben a GDI-objektumok és a memória stabil (nem nő a Timer miatt). |
Gyakori hibák és önértékelés
Gyakori hibák a projektben
- Mindent egyszerre javítanak: nem derül ki, melyik hiba volt melyik (egy javítás, egy ellenőrzés).
- A logikai hibát találgatással javítják, nem a debuggerrel (hipotézis nélkül).
- Az
ifellenőrzés elfedi a hibát, de nincs naplózva, hogy előfordult. - A
catch (Exception)elnyel mindent (programozói hibát is). - A teszt várt értékét a program kimenetéből másolják.
- A javítás után nincs regressziós futtatás.
- A hibajegy „nem működik” szintű, nem reprodukálható.
Önértékelés
Pontozd magad a követelménytábla szerint, majd válaszolj:
- Melyik hibát találtad meg a legnehezebben, és milyen eszköz segített végül?
- Melyik hibát lehetett volna megelőzni (ellenőrzés, típus, teszt), és hogyan?
- Mit mutatott a napló, amit a debugger nem?
- Hogyan változott a program, miután a naplózás és a hibakezelés bekerült?
- Melyik tesztesetet bővítenéd a következő kiadáshoz?
Bővítési ötletek
- Automatikus tesztek: a
Diak.Atlagés azEllenorzoegységtesztjei (xUnit vagy MSTest), a javított hibák regressziós tesztjeivel. - Idézőjeles CSV: a név tartalmazhasson
;jelet (10.5 lecke). - Naplónéző: a 13.7 lecke naplónézője a programba építve.
- Visszavonás: az utolsó jegy törlése, a diák törlése megerősítéssel.
- Aszinkron mentés: a mentés háttérszálon (11. fejezet), megszakítható.
- Statisztikák: jegyek eloszlása (oszlopdiagram, 9.9 lecke), osztályátlag.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Melyik eszköz?
Melyik eszközzel találod meg: (a) hiányzó pontosvessző; (b) rossz átlag; (c) a fájl nem írható? (a) Error List; (b) debugger (töréspont, Watch); (c) a kivétel és a napló (valamint teszt a védett mappával).Megoldás megnyitása
2. Hibafajta
Sorold be: egész osztás az átlagnál; üres lista átlaga; Logikai; futási (int jegy = txtJegy.Text.Megoldás megnyitása
DivideByZeroException); fordítási (CS0029).
Közepes szint
3. Megelőzni vagy elkapni?
A kijelölés nélküli jegyrögzítés és a védett mappába mentés közül melyiket előzöd meg, melyiket kapod el? A kijelölés hiányát megelőzöd (Megoldás megnyitása
if (SelectedIndex < 0), programozói/bevitel hiba); a védett mappát elkapod (UnauthorizedAccessException, környezeti hiba).
4. Hipotézis
A „legjobb tanuló” rossz. Írj hipotézist és az ellenőrzését. Hipotézis: a rendezés növekvő, ezért a legkisebb átlagú az első. Ellenőrzés: az Immediate ablakban Megoldás megnyitása
? diakok.OrderBy(x => x.Atlag()).First().Nev és ? diakok.OrderByDescending(...); a két kifejezés eredménye eltér, és a második a helyes.
Kihívás szint
5. Regresszió
A 10 javítás után hogyan bizonyítod, hogy a program ugyanúgy működik, ahol kell? Regressziós készlet: smoke teszt (indul, felvesz, jegyet rögzít, ment, betölt) és a javított hibák tesztesetei; minden javítás után újrafuttatod, és a státuszt rögzíted.Megoldás megnyitása
6. Következő lépés
Hogyan akadályoznád meg, hogy a hibák visszatérjenek? Automatikus tesztek a logikára (a hibás átlag, a rendezés), fordítási szigor (figyelmeztetések hibaként), kódáttekintés, és a napló rendszeres átnézése; minden javított hibához új teszteset.Megoldás megnyitása
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A fordítási hibákkal, azon belül a legelsővel; addig a többi nem látszik. Debuggerrel: töréspont, léptetés, Locals/Watch, hipotézis és ellenőrzés. Környezeti hibáknál (fájl, jogosultság, adatbázis); a várható bevitelt ellenőrzés védi. Indulási adatokat, fontos műveleteket, minden kivételt Feladatkezelővel (memória, GDI) és Diagnostic Tools pillanatképekkel, hosszú futás közben; a javítás után újra mérsz. Egy hiba, reprodukálható lépésekkel, környezettel, elvárt és tényleges eredménnyel, súlyossággal. Mert a javítás véletlenül elronthat más funkciókat; a készlet ezt gyorsan kimutatja.1. Melyik hibával kezded?
2. Hogyan találsz logikai hibát?
3. Mikor kell try–catch?
4. Mit naplózol?
ex.ToString()-gal; személyes adatot nem.5. Hogyan találod meg a szivárgást?
6. Mi a jó hibajegy?
7. Miért kell regressziós teszt?
A 13. fejezet összefoglalása: mit tudsz már?
- Fordítási hibákat az Error List-tel, logikai hibákat a debuggerrel (töréspont, Locals, Watch, Call Stack) keresel és javítasz.
- Tudatosan kezeled a kivételeket (
try–catch–finally), és védőhálót állítasz be. - Ellenőrzöd a bevitelt (üres, hibás, szélsőérték), és biztonságosan kezeled a fájlokat és adatbázist.
- Naplózol, és a naplóból olvasod ki a hibák okát.
- Módszeresen teszteled a programot (tesztesetek, hibajegyek, regresszió).
- Méred és javítod az erőforrás-használatot és a teljesítményt.
Következő fejezet: Build, közzététel és telepítés
A 14. fejezetben a kész programod másik gépen is működik: Release fordítás, közzététel, telepítő és a programverziók kezelése.

