Grafikus C# – Windows Forms tanfolyam › 13.10 lecke

Windows Forms · 13.10 lecke · záró projekt

Mini projekt: hibás alkalmazás javítása

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.

  • kb. 5–6 óra
  • záró projekt
  • hibás alkalmazás
Anime stílusú diák hibavadászként keresi a hibát a kódban

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.

13.9 Erőforrás-kezelés →

Követelménytábla (100 pont)

KövetelményPont
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.
Hibajegy-sablon (egy jegy = egy hiba)sablon
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]
📷 Képernyőkép helye

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.

1. lépés: felépítés és az első fordítás

Hozd létre a Formot a leírt vezérlőkkel (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)

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.

3. lépés: naplózás bevezetése

Adj a programhoz 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ő

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 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)

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).

6. lépés: bevitel-ellenőrzés

Központi 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

Biztonságos mentés a felhasználói mappába (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

Mérd a program memóriáját és GDI-objektumait hosszabb futás közben (Feladatkezelő): a 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.

A hibás program (ezt másold a Form1.cs-be)
Form1.cs (HIBÁS)C#
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óHibaFajta / leckeEszköz
C1InitializeComponent() után hiányzik a ;fordítási (CS1002), 13.1Error List
C2legjobb.Nevv elírásfordítási (CS1061), 13.1Error List
C3int jegy = txtJegy.Text;fordítási (CS0029), 13.1Error List
C4hiányzó using System.Linq; (OrderBy, First)fordítási (CS1061), 13.1Ctrl+.
L1az átlag egész osztással számol (osszeg / Count)logikai, 13.2–13.3töréspont, Watch
L2a OrderBy növekvő: a legrosszabb tanulót adjalogikai, 13.3Immediate, Locals
R1üres jegylista átlaga: DivideByZeroException; a lista frissítése is leállfutási, 13.4Exception Helper
R2a diakok lista nincs létrehozva: NullReferenceExceptionfutási, 13.3–13.4debugger, Locals
R3nincs kijelölés: SelectedIndex = -1 → ArgumentOutOfRangeExceptionfutási, 13.4Exception Helper
V1a név nincs ellenőrizve (üres, szóköz, hosszú, duplikált, ;)bevitel, 13.5határértékek
V2a jegy nincs ellenőrizve (TryParse, 1–5)bevitel, 13.5határértékek
F1mentés a C:\ gyökerébe: UnauthorizedAccessException; nincs tryfájl, 13.6teszt, napló
F2a név ; vagy sortörés jele elrontja a CSV-t; a mentés nem biztonságos (csonka fájl)fájl, 13.6teszt
F3a betöltés nem létező fájlra FileNotFoundExceptionfájl, 13.6teszt
F4–F5sérült sor: IndexOutOfRange, üres jegylista: FormatExceptionfájl és bevitel, 13.5–13.6sérült fájl teszt
N1nincs naplózás, nincs globális hibakezelődiagnosztika, 13.4, 13.7áttekintés
S1a 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.9Feladatkezelő (GDI)
A javított program (próbáld ki előbb önállóan!)
Form1.cs (JAVÍTVA; a naplózás a 13.7 lecke FajlNaplo osztályát használja)C#
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());
}

Megjegyzések: a név a mintában tiltja a ; é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).

#TesztElvárt eredmény
1A projekt lefordul0 hiba, 0 figyelmeztetés (Error List).
2Új diák érvényes névvelFelkerü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 karakterHibaüzenet és ErrorProvider jel; nem kerül a listára.
4Név ; jellel, sortörésselElutasítva (nem rontja el a CSV-t).
5Azonos név másodszor (kis-nagybetű eltéréssel)„Ilyen nevű diák már van.”
6Jegy kijelölés nélkül„Előbb jelölj ki egy diákot.”; nincs kivétel.
7Jegy: 0, 1, 5, 6, üres, „abc”, „4,5”1 és 5 elfogadva; a többi hiba, érthető üzenettel.
8Átlag: 5, 4 jegy után4,50 (nem 4); két tizedesre kerekítve.
9Átlag jegy nélküli diáknál(nincs jegy); nincs DivideByZeroException.
10Legjobb tanuló több diákkalA legmagasabb átlagú (nem a legalacsonyabb); a jegy nélküliek kimaradnak; döntetlennél betűrend.
11Legjobb tanuló üres listánál„Még nincs átlag.”; nincs kivétel.
12Mentés, majd újraindítás és betöltésMinden diák és jegy megvan; a fájl a felhasználói mappában van.
13Mentés védett mappába (útvonal átírva)Érthető hibaüzenet, a program fut tovább; a naplóban Error sor.
14Betöltés nem létező fájlból„Még nincs mentett fájl.”; nincs kivétel.
15Betö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.
16Szándékos váratlan hiba (teszt-gomb)A globális kezelő üzenetet ad, naplóz; a program nem omlik össze.
17Napló ellenőrzéseVan naplófájl; indulási adatok, műveletek, a hibák ex.ToString()-gal; nincs diáknév benne.
18Erőforrás-teszt: 10 perc futásA 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 if ellenő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 az Ellenorzo egysé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ó?

Megoldás megnyitása

(a) Error List; (b) debugger (töréspont, Watch); (c) a kivétel és a napló (valamint teszt a védett mappával).

2. Hibafajta

Sorold be: egész osztás az átlagnál; üres lista átlaga; int jegy = txtJegy.Text.

Megoldás megnyitása

Logikai; futási (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?

Megoldás megnyitása

A kijelölés hiányát megelőzöd (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.

Megoldás megnyitása

Hipotézis: a rendezés növekvő, ezért a legkisebb átlagú az első. Ellenőrzés: az Immediate ablakban ? 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?

Megoldás megnyitása

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.

6. Következő lépés

Hogyan akadályoznád meg, hogy a hibák visszatérjenek?

Megoldás megnyitása

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.

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Melyik hibával kezded?

A fordítási hibákkal, azon belül a legelsővel; addig a többi nem látszik.

2. Hogyan találsz logikai hibát?

Debuggerrel: töréspont, léptetés, Locals/Watch, hipotézis és ellenőrzés.

3. Mikor kell try–catch?

Környezeti hibáknál (fájl, jogosultság, adatbázis); a várható bevitelt ellenőrzés védi.

4. Mit naplózol?

Indulási adatokat, fontos műveleteket, minden kivételt ex.ToString()-gal; személyes adatot nem.

5. Hogyan találod meg a szivárgást?

Feladatkezelővel (memória, GDI) és Diagnostic Tools pillanatképekkel, hosszú futás közben; a javítás után újra mérsz.

6. Mi a jó hibajegy?

Egy hiba, reprodukálható lépésekkel, környezettel, elvárt és tényleges eredménnyel, súlyossággal.

7. Miért kell regressziós teszt?

Mert a javítás véletlenül elronthat más funkciókat; a készlet ezt gyorsan kimutatja.

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.