Grafikus C# – Windows Forms tanfolyam › 13.4 lecke

Windows Forms · 13.4 lecke · kivételkezelés

try, catch és finally

A kivételkezelés alapjait a 4. fejezetben már megismerted. Most tudatosan használjuk: mikor érdemes elkapni a kivételt, és mikor kell inkább megelőzni; hogyan működik több catch blokk és a finally; mit jelent a throw és a throw ex közötti különbség; hogyan készítesz saját kivételtípust; és hogyan állítasz be végső védőhálót a Program.cs-ben, hogy a program soha ne omoljon össze kezeletlen kivétel ablakkal. A cél a hibatűrő program: amikor valami elromlik, a felhasználó érthető üzenetet kap, az adatai nem vesznek el, és a fejlesztő a naplóból megtudja, mi történt.

  • kb. 100 perc
  • közepes–haladó szint
  • hibatűrés
Anime stílusú fiú ötletel

A lecke végére

  • helyesen használsz több catch blokkot (specifikustól az általánosig) és kivételszűrőt (when);
  • a finally blokkban takarítasz (kurzor, gombok, fájl, kapcsolat), és érted, mikor fut le;
  • eldöntöd, hogy a hibát megelőzöd (ellenőrzés, TryParse) vagy elkapod;
  • különbséget teszel a throw; és a throw ex; között, és beágyazod az eredeti kivételt (InnerException);
  • saját kivételtípust írsz;
  • globális hibakezelőt állítasz be (Application.ThreadException, AppDomain.UnhandledException) és naplózol.

Mielőtt elkezded

Ehhez a leckéhez a 4.7 lecke (kivételkezelés alapjai), a 13.1–13.3 lecke és a 10.9 lecke (mentési hibák) szükséges.

13.3 Locals, Watch →

A try–catch–finally szerkezete

Form1.csC#
private void btnBeolvas_Click(object sender, EventArgs e)
{
btnBeolvas.Enabled = false;
this.Cursor = Cursors.WaitCursor;

try
{
string tartalom = File.ReadAllText(txtUt.Text);
int szam = int.Parse(tartalom.Trim());
lblEredmeny.Text = (100 / szam).ToString();
}
catch (FileNotFoundException) // 1. a LEGSPECIFIKUSABB elöl
{
lblEredmeny.Text = "A fájl nem található.";
}
catch (FormatException)
{
lblEredmeny.Text = "A fájl nem számot tartalmaz.";
}
catch (DivideByZeroException)
{
lblEredmeny.Text = "Nullával nem lehet osztani.";
}
catch (IOException ex) when (ex.HResult == unchecked((int)0x80070020)) // kivételszűrő: csak ezt az esetet (fájlzár)
{
lblEredmeny.Text = "A fájlt másik program használja.";
}
catch (IOException ex) // 2. az általánosabb IOException UTÁN
{
lblEredmeny.Text = "Olvasási hiba: " + ex.Message;
}
finally
{
this.Cursor = Cursors.Default; // MINDIG lefut: siker, hiba, vagy return esetén is
btnBeolvas.Enabled = true;
}
}
A catch sorrendjeA kivétel az első illeszkedő catch-hez kerül. Ezért a specifikus típus előre, az általános hátra: a FileNotFoundException az IOException leszármazottja, ezért előbb kell állnia. Fordítva a fordító hibát ad (CS0160: egy korábbi catch már elkapja).
Kivételszűrő (when)A catch (...) when (feltétel) csak akkor kapja el a kivételt, ha a feltétel igaz; különben továbbmegy a következő catch-hez. Így ugyanazon típust a részletei szerint különválaszthatod, és a stack trace érintetlen marad.
finally: mindigA finally blokk lefut, ha a try sikeresen befejeződik, ha kivétel keletkezik (elkapva vagy nem), és ha a try-ban return áll. Ott takarítasz: kurzor, gombok, megnyitott fájl vagy kapcsolat. A using utasítás valójában try–finally.
Mikor nem fut a finally?Ha a folyamat azonnal leáll (Environment.Exit, Environment.FailFast, áramszünet, a folyamat erőszakos leállítása), vagy végtelen ciklus/holtpont van a try-ban. Egyébként mindig.

Jósolj!

A try blokkban return; áll, a finally-ban pedig btnBeolvas.Enabled = true;. Lefut-e a finally?

Jóslat ellenőrzése

Igen: a finally a return előtt lefut (a metódus csak utána lép ki). Ezért ideális a gombok visszaengedésére: nem maradhat tiltva a gomb, bármi történik.

Elkapni vagy megelőzni?

KivételEredetMit tegyél?
FormatException, OverflowException (szám-szövegből)A felhasználó hibás bevitele: várható.Előzd meg: int.TryParse, nincs kivétel.
NullReferenceException, IndexOutOfRangeException, ArgumentNullExceptionProgramozói hiba.Javítsd a kódot (ellenőrzés, helyes index), ne kapd el.
DivideByZeroExceptionHiányzó ellenőrzés (programozói hiba).if (n != 0) ellenőrzés; csak végszükségben elkapni.
FileNotFoundException, IOException, UnauthorizedAccessExceptionKörnyezeti hiba: a program nem befolyásolhatja (lemez, jog, zárolás).Kapd el, jelezd, tegyél valamit (újrapróbálás, másik hely).
HttpRequestException, TimeoutException, adatbázis-hibaKülső rendszer hibája.Kapd el, és kezeld (újrapróbálás, offline mód).
InvalidOperationException, KeyNotFoundExceptionRossz állapot vagy hiányzó kulcs.Előzd meg (TryGetValue, állapot-ellenőrzés), vagy kapd el, ha az állapot külső.
Form1.csC#
// ROSSZ: a várható hibát kivétellel kezeljük (lassú, zajos, nehezen olvasható)
try { szam = int.Parse(txtSzam.Text); }
catch (FormatException) { lblHiba.Text = "Számot adj meg!"; return; }

// JÓ: a várható hibát ellenőrzéssel kezeljük
int szam;

if (!int.TryParse(txtSzam.Text, out szam)) { lblHiba.Text = "Számot adj meg!"; return; }

// A környezeti hiba KIVÉTEL (nem lehet előre biztosan tudni): ott try–catch
try { File.WriteAllText(ut, szoveg); }
catch (UnauthorizedAccessException) { lblHiba.Text = "Nincs jogosultság a mentéshez."; }
catch (IOException ex) { lblHiba.Text = "A mentés nem sikerült: " + ex.Message; }

// A rossz minta: minden elnyelése
try { Mindent(); } catch { } // ✗✗✗ a hiba eltűnik, semmit sem tudsz róla
try { Mindent(); } catch (Exception) { } // ✗ ugyanaz
Az elvA kivétel kivételes helyzetre való. Amit előre sejthetsz (hibás bevitel, üres lista, nulla osztó), azt ellenőrzéssel kezeld. A try–catch azokra a hibákra való, amelyeket nem tudsz előre kizárni (környezet, külső rendszer).
Csak akkor kapd el, ha tudsz mit tenniA catch értelme: kezelni (újrapróbálás, alapértelmezett érték, érthető üzenet a felhasználónak) vagy naplózni és továbbdobni. Ha nincs mit tenni vele, hagyd felfelé szállni: a globális kezelő fogja.
Soha ne nyeld elAz üres catch { } a legrosszabb: a hiba „eltűnik”, a program tovább fut hibás állapotban, és a hibakeresés lehetetlen (13.3 lecke). Legalább naplózz.
Ne kapj túl szélesenA catch (Exception) mindent elkap, a programozói hibákat is (NullReferenceException), és elfedi őket. Fogd a konkrét típusokat. Széles catch csak a legfelső szinten (globális kezelő) indokolt.

Jósolj!

Egy ciklus 10 000 felhasználói beírást dolgoz fel int.Parse + catch (FormatException) módszerrel, és a bemenetek fele hibás. Mi a gond az int.TryParse-hoz képest?

Jóslat ellenőrzése

A kivétel drága (a keletkezése és a stack trace összeállítása ezerszer lassabb lehet a sima összehasonlításnál): 5000 kivétel érezhetően lassít. Ráadásul a hibakeresőben „Break When Thrown” mellett minden hibás beírás megállítja a programot. A TryParse kivétel nélkül jelzi a hibát.

Dobás, továbbdobás, saját kivétel

Kivetelek.cs és Form1.csC#
// SAJÁT KIVÉTEL: a név megmondja, mi a baj; plusz adat a hibáról
public class ErvenytelenBevitelException : Exception
{
public string Mezo { get; private set; }

public ErvenytelenBevitelException(string mezo, string uzenet) : base(uzenet) { Mezo = mezo; }
public ErvenytelenBevitelException(string mezo, string uzenet, Exception ok) : base(uzenet, ok) { Mezo = mezo; }
}

// DOBÁS: ha a metódus nem tudja teljesíteni a feladatát, kivételt dob (nem ad vissza hibakódot)
public static int Kor(string szoveg)
{
int kor;

if (!int.TryParse(szoveg, out kor)) { throw new ErvenytelenBevitelException("kor", "Az életkor egész szám kell legyen."); }
if (kor < 0 || kor > 150) { throw new ErvenytelenBevitelException("kor", "Az életkor 0 és 150 között lehet."); }

return kor;
}

// TOVÁBBDOBÁS: naplózol, de a hívó is értesüljön
try { Mentes(); }
catch (IOException ex)
{
Naploz(ex);
throw; // ✓ az eredeti kivétel és stack trace megmarad
// throw ex; // ✗ a stack trace ELVÉSZ: a hiba helyének nyoma eltűnik
}

// BEÁGYAZÁS: más típusú kivétellé alakítod, de az eredeti az InnerException-ben marad
try { json = JsonSerializer.Deserialize<Beallitas>(szoveg); }
catch (JsonException ex)
{
throw new TaroloHiba("A beállításfájl sérült.", ex); // ex az InnerException
}
throw; és throw ex;A throw; (változó nélkül) ugyanazt a kivételt dobja tovább, eredeti stack trace-szel. A throw ex; mintha újra dobnád: a stack trace az újradobás helyéről indul, az eredeti hely elvész. Mindig throw;.
BeágyazásHa a kivétel típusát átalakítod (JsonException → TaroloHiba), az eredetit add át második paraméterként (InnerException): a naplóban látszik a valódi ok, a hívó pedig a saját rétegének típusát kapja (12.8 lecke).
Saját kivételAkkor érdemes, ha a hívónak más választ kell adnia a hibára (ErvenytelenBevitelException: jelöld a mezőt; TaroloHiba: mentési hiba üzenet). Névvége: Exception; az Exception-ből (nem ApplicationException-ből) származtasd.
Dobás vagy hibakód?A modern C# kivételt dob, ha a metódus nem tudja teljesíteni a feladatát (érvénytelen bemenet, hiányzó erőforrás). A bool TrySomething(out ...) minta a várható hibákhoz való (TryParse), ahol a hiba a normális működés része.

Jósolj!

A catch (Exception ex) { Naploz(ex); throw ex; } kódot használod egy mély metódusban. A naplóban a stack trace első sora a catch blokk sora, nem a hiba valódi helye. Miért?

Jóslat ellenőrzése

A throw ex; újraindítja a stack trace-t az adott soron: az eredeti hely elvész. A throw; megőrzi. A hibakeresés szempontjából a különbség óriási: a valódi hely nélkül órákig keresheted a hibát.

Globális hibakezelő: a végső védőháló

Program.csC#
static class Program
{
[STAThread]
static void Main()
{
// a felületi szál kezeletlen kivételei ide futnak (nem omlik össze a program a hibaablakkal)
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += (s, e) => Vegzetes(e.Exception);

// más szálak kezeletlen kivételei (ezeket már nem lehet megállítani, csak naplózni)
AppDomain.CurrentDomain.UnhandledException += (s, e) => Naploz(e.ExceptionObject as Exception);

Application.EnableVisualStyles();
Application.Run(new Form1());
}

private static void Vegzetes(Exception ex)
{
Naploz(ex); // 1. a részletek a naplóba (a fejlesztőnek)

DialogResult v = MessageBox.Show( // 2. érthető üzenet a felhasználónak, stack trace nélkül
"Váratlan hiba történt. A részleteket naplóba írtuk. Folytatod a használatot?",
"Hiba", MessageBoxButtons.YesNo, MessageBoxIcon.Error);

if (v == DialogResult.No) { Application.Exit(); } // 3. a felhasználó dönt; nem biztos, hogy biztonságos folytatni
}

private static void Naploz(Exception ex)
{
try
{
string mappa = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MiProgram");

Directory.CreateDirectory(mappa);
File.AppendAllText(Path.Combine(mappa, "hiba.log"), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + ex + Environment.NewLine + Environment.NewLine); // ex.ToString(): típus, üzenet, stack trace, inner
}
catch { /* a naplózás hibája ne okozzon újabb hibát */ }
}
}
Mire jó?Ha egy kivétel sehol sem lett elkapva, a Windows Forms alapból a „kezeletlen kivétel” ablakot mutatja (Details / Continue / Quit), vagy összeomlik. A globális kezelő ehelyett naplóz, és barátságos üzenetet ad: ez a végső védőháló, nem a normális hibakezelés.
ThreadExceptionA felületi szál kezeletlen kivételei (eseménykezelőkben): a kezelőben a program folytathatja a futást. Az UnhandledExceptionMode.CatchException beállítás biztosítja, hogy ide kerüljön (a konfigurációtól függetlenül).
UnhandledExceptionA háttérszálak kezeletlen kivételei (Thread, Task.Run nem várt kivétele, 11. fejezet). Itt már nem lehet megállítani az összeomlást, csak naplózni. Ezért kell a háttérmunkában try–catch.
Mit mutass a felhasználónak?Érthető, rövid üzenetet, tanácsot (mentsd a munkát, indítsd újra), nem stack trace-t. A technikai részlet a naplóba kerül; a naplófájl helyét a hibaüzenet megadhatja, ha a felhasználó segítséget kér.

Jósolj!

A Naploz metódus a lemezre ír, a lemez tele van, és a naplózás maga is kivételt dob. Mi történik, ha a Naploz belsejében nincs try–catch?

Jóslat ellenőrzése

A hibakezelő hibázik: újabb kezeletlen kivétel keletkezik, amely a kezelőt újra hívhatja, vagy a program összeomlik. Az egyetlen hely, ahol az „elnyelő” catch indokolt: a naplózó belsejében, hogy a védőháló ne szakadjon el.

📷 Képernyőkép helye

Mit tegyél ide: a hibatűrő átváltó: egy kezelt hiba üzenete és a globális hibaablak a naplózott hibával.Javasolt fájlnév: winforms-13-4-hibatures.webp · Alt szöveg: A hibatűrő átváltó program

Hibakeresés: te vagy a hibadetektív

Mi a baj, és hogyan javítod?

Gondold végig az okot, és csak utána nyisd le a megoldást.

1. CS0160: egy korábbi catch már elkapja

catch (IOException) { }
catch (FileNotFoundException) { } // sosem érhető el
Hiba és javítás

A leszármazott típus (FileNotFoundException) az általános (IOException) elé kerüljön.

2. A program nem jelez semmit

try { Mentes(); } catch { }
Hiba és javítás

A hiba elnyelődik. Kapd el a konkrét kivételt, jelezz a felhasználónak, naplózz; ha nem tudsz mit tenni, ne kapd el.

3. A gomb tiltva marad

btnMent.Enabled = false;
try { Mentes(); btnMent.Enabled = true; }
catch (IOException) { lblHiba.Text = "Hiba"; }
Hiba és javítás

Hibánál a Enabled = true sor kimarad. A takarítás a finally-ba való.

4. A stack trace elveszik

catch (Exception ex) { Naploz(ex); throw ex; }
Hiba és javítás

throw; kell, mert a throw ex; újraindítja a stack trace-t.

5. A felhasználó stack trace-t lát

catch (Exception ex) { MessageBox.Show(ex.ToString()); }
Hiba és javítás

A felhasználónak érthető üzenet kell, a részletek a naplóba mennek. A hosszú technikai szöveg ijesztő és használhatatlan.

6. Kivétel a ciklusban, mindig megáll

Egy 1000 elemes feldolgozásban egy hibás elem leállítja az egészet.

Ok és javítás

A try–catch a ciklus körül áll, nem benne. Tedd az elemenkénti feldolgozás köré, így a hibás elemet kihagyod és naplózod, a többi lefut.

Mini projekt: hibatűrő átváltó

Készíts egységátváltó programot, amelyet nem lehet „összeomlasztani”: minden hibás bemenetre és környezeti hibára érthető választ ad, és a váratlan hibákat naplózza:

  1. Felület: cmbMuvelet (Hőmérséklet °C→°F, Távolság km→mérföld, Arány (osztás), Fájlból olvasás), txtBemenet, txtMasodik (osztáshoz), txtUt (fájlhoz), Számol, Mentés naplóba; lblEredmeny, lblHiba; állapotsor; lstNaplo.
  2. Saját kivétel: ErvenytelenBevitelException (a Mezo tulajdonsággal); a Beolvas segédmetódus a hibás szöveget ezzel jelzi (TryParse, nem Parse).
  3. Logika: az átváltások külön statikus osztályban (12.7 lecke): a szükséges ellenőrzések (osztó ≠ 0, tartomány: hőmérséklet ≥ −273,15 °C) ErvenytelenBevitelException vagy ArgumentOutOfRangeException kivételt dobnak.
  4. Specifikus catch-ek: a Számol kezelője külön ágon kezeli az ErvenytelenBevitelException (a hibás mezőt jelöli az ErrorProvider), a FileNotFoundException, az UnauthorizedAccessException és az IOException eseteit (when szűrővel a fájlzárat: A fájlt másik program használja).
  5. finally: a Számol gomb tiltása és a várakozó kurzor visszaállítása mindig a finally-ban.
  6. Továbbdobás és beágyazás: a fájlolvasó metódus a FormatException-t ErvenytelenBevitelException-né alakítja (az eredeti az InnerException); egy másik ponton throw; naplózás után.
  7. Globális kezelő: a Program.cs-ben ThreadException és UnhandledException kezelő, napló a %AppData% mappába; egy rejtett Összeomlás-teszt gomb szándékosan InvalidOperationException-t dob (ellenőrzéshez).
  8. Naplónézet: a Mentés naplóba a legutóbbi kezelt hibákat a lstNaplo listában mutatja (a naplózó metódus belsejében védett catch).
  9. Teszteld: hibás szám, üres mező, 0 osztó, −300 °C, nem létező fájl, olvasásvédett fájl, megnyitott (zárolt) fájl, szöveges fájl számot várva, az Összeomlás-teszt, és ellenőrizd, hogy a gombok minden esetben visszaállnak.
Megoldás megnyitása (próbáld ki előbb önállóan!)
Atvalto.cs és Form1.cs (using System.IO; using System.Windows.Forms;)C#
public class ErvenytelenBevitelException : Exception
{
public string Mezo { get; private set; }

public ErvenytelenBevitelException(string mezo, string uzenet) : base(uzenet) { Mezo = mezo; }
public ErvenytelenBevitelException(string mezo, string uzenet, Exception ok) : base(uzenet, ok) { Mezo = mezo; }
}

public static class Atvalto
{
public static double Beolvas(string szoveg, string mezo)
{
double d;

if (!double.TryParse((szoveg ?? "").Trim(), out d)) { throw new ErvenytelenBevitelException(mezo, "A(z) " + mezo + " mezőbe számot írj."); }

return d;
}

public static double CelsiusFahrenheit(double c)
{
if (c < -273.15) { throw new ErvenytelenBevitelException("hőmérséklet", "Az abszolút nulla (−273,15 °C) alatt nincs hőmérséklet."); }

return c * 9 / 5 + 32;
}

public static double KmMerfold(double km)
{
if (km < 0) { throw new ErvenytelenBevitelException("távolság", "A távolság nem lehet negatív."); }

return km * 0.621371;
}

public static double Oszt(double a, double b)
{
if (b == 0) { throw new ErvenytelenBevitelException("osztó", "Nullával nem lehet osztani."); }

return a / b;
}

public static double FajlbolSzam(string ut)
{
string tartalom = File.ReadAllText(ut);

try { return double.Parse(tartalom.Trim()); }
catch (FormatException ex) { throw new ErvenytelenBevitelException("fájl", "A fájl nem számot tartalmaz.", ex); } // beágyazás: az eredeti az InnerException
}
}

// ---- Form1.cs ----
private readonly List<string> kezeltHibak = new List<string>();

private void btnSzamol_Click(object sender, EventArgs e)
{
btnSzamol.Enabled = false;
Cursor = Cursors.WaitCursor;
errorProvider1.Clear();
lblHiba.Text = "";

try
{
switch (cmbMuvelet.SelectedIndex)
{
case 0: lblEredmeny.Text = Atvalto.CelsiusFahrenheit(Atvalto.Beolvas(txtBemenet.Text, "hőmérséklet")).ToString("0.##") + " °F"; break;
case 1: lblEredmeny.Text = Atvalto.KmMerfold(Atvalto.Beolvas(txtBemenet.Text, "távolság")).ToString("0.##") + " mérföld"; break;
case 2: lblEredmeny.Text = Atvalto.Oszt(Atvalto.Beolvas(txtBemenet.Text, "osztandó"), Atvalto.Beolvas(txtMasodik.Text, "osztó")).ToString("0.####"); break;
case 3: lblEredmeny.Text = Atvalto.FajlbolSzam(txtUt.Text).ToString("0.##"); break;
default: lblHiba.Text = "Válassz műveletet."; break;
}
}
catch (ErvenytelenBevitelException ex)
{
lblEredmeny.Text = "";
lblHiba.Text = ex.Message;
errorProvider1.SetError(ex.Mezo == "osztó" ? txtMasodik : ex.Mezo == "fájl" ? (Control)txtUt : txtBemenet, ex.Message);
Naploz("Érvénytelen bevitel (" + ex.Mezo + "): " + ex.Message);
}
catch (FileNotFoundException) { lblHiba.Text = "A fájl nem található."; Naploz("Fájl nem található: " + txtUt.Text); }
catch (DirectoryNotFoundException) { lblHiba.Text = "A mappa nem található."; Naploz("Mappa nem található: " + txtUt.Text); }
catch (UnauthorizedAccessException) { lblHiba.Text = "Nincs jogosultság a fájlhoz."; Naploz("Jogosultsághiba: " + txtUt.Text); }
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 32 || (ex.HResult & 0xFFFF) == 33) // 32/33: a fájlt másik folyamat használja / zárolta
{
lblHiba.Text = "A fájlt másik program használja. Zárd be, és próbáld újra.";
Naploz("Fájlzár: " + txtUt.Text);
}
catch (IOException ex) { lblHiba.Text = "Olvasási hiba: " + ex.Message; Naploz(ex.ToString()); }
finally
{
Cursor = Cursors.Default; // mindig
btnSzamol.Enabled = true;
}
}

private void Naploz(string s)
{
kezeltHibak.Insert(0, DateTime.Now.ToString("HH:mm:ss") + " " + s);
lstNaplo.Items.Clear();
lstNaplo.Items.AddRange(kezeltHibak.Take(50).ToArray());
}

private void btnOsszeomlas_Click(object sender, EventArgs e)
{
throw new InvalidOperationException("Szándékos teszthiba a globális kezelő kipróbálásához.");
}
📷 Képernyőkép helye

Mit tegyél ide: a kész hibatűrő átváltó több kezelt hibával és a naplóval.Javasolt fájlnév: winforms-13-4-kesz.webp · Alt szöveg: A kész hibatűrő átváltó

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.

1. Elemenkénti hibakezelés

Egy 500 soros szövegfájlt dolgozol fel soronként; néhány sor hibás. Hogyan haladsz tovább a hibás soroknál?

Egy lehetséges megoldás

A try–catch a ciklus testében áll (soronként): a hibás sort naplózod (sorszám, ok), és továbblépsz; a végén összegzed: 480 sor feldolgozva, 20 hibás (lásd a naplót).

2. Újrapróbálás

Egy hálózati letöltés időnként HttpRequestException-t dob. Írj háromszori újrapróbálást növekvő várakozással.

Egy lehetséges megoldás

Ciklus 1–3, try { ...; return; } catch (HttpRequestException) when (proba < 3) { await Task.Delay(proba * 1000); }; a harmadik hibánál a kivétel felfelé szál (a when már nem teljesül).

3. Végső hibaablak

Készíts egyedi hibaablakot a globális kezelőhöz: üzenet, Részletek (összecsukható), Napló megnyitása.

Egy lehetséges megoldás

Külön HibaForm (ShowDialog), a kivétel szövege (ex.ToString()) egy összecsukható TextBoxban; a Napló megnyitása a Process.Start-tal a mappát nyitja (a felhasználónak nem kell keresnie).

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

Milyen sorrendben írod a catch (Exception) és a catch (FormatException) blokkot?

Megoldás megnyitása

Előbb a FormatException, utána az Exception: a specifikus típus elöl, az általános hátul.

2. Mikor fut a finally?

Fut-e, ha a try-ban kivétel van, és nincs hozzá catch?

Megoldás megnyitása

Igen: a finally lefut, mielőtt a kivétel továbbhalad a hívóhoz.

Közepes szint

3. Megelőzni vagy elkapni?

Melyik: (a) üres lista első elemének olvasása; (b) hálózati fájl megnyitása?

Megoldás megnyitása

(a) megelőzni (if (lista.Count > 0)); (b) elkapni (környezeti hiba, nem előre tudható).

4. throw vagy throw ex?

Miért throw;?

Megoldás megnyitása

Megőrzi az eredeti stack trace-t; a throw ex; újraindítja a hívási lánc nyomát.

Kihívás szint

5. Kivétel a finally-ban

Mi történik, ha a finally blokk is kivételt dob?

Megoldás megnyitása

Az új kivétel felülírja az eredetit (az eredeti elveszik). Ezért a finally legyen egyszerű és biztonságos; a kockázatos takarítást védd külön try–catch-csel.

6. Háttérszál

Miért nem fogja el a try–catch a new Thread(...) belsejében dobott kivételt, ha a try a szálindítás körül áll?

Megoldás megnyitása

A kivétel a másik szálon keletkezik, a hívási lánca nem a try alatt fut. A try–catch a szál belsejébe (a munka köré) kell; a Task-nál az await hozza vissza a kivételt a hívóhoz (11.5 lecke).

Gyors önellenőrzés

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

1. Mire való a finally?

Takarításra: mindig lefut (siker, hiba, return esetén is).

2. Hogyan sorrendezed a catch blokkokat?

A leggyakoribb/legspecifikusabb elöl, az általános hátul.

3. Mikor ne kapd el a kivételt?

Ha programozói hiba (null, index) vagy nincs mit tenni vele: javítsd a kódot, vagy hagyd felszállni.

4. Mi a throw; és a throw ex; különbsége?

A throw; megőrzi a stack trace-t, a throw ex; újraindítja.

5. Mire jó az InnerException?

Az eredeti kivételt hordozza, ha új típusúvá alakítod.

6. Mi a globális hibakezelő?

A kezeletlen kivételek végső védőhálója (ThreadException, UnhandledException): naplóz és érthetően jelez.

7. Mit mutatsz a felhasználónak?

Érthető üzenetet; a technikai részletek a naplóba mennek.

Mit tudsz már?

  • Több catch-et és kivételszűrőt használsz, és a finally-ban takarítasz.
  • Eldöntöd, mikor előzöd meg a hibát, és mikor kapod el.
  • throw; és beágyazás segítségével megőrzöd az eredeti hibát; saját kivételtípust írsz.
  • Globális hibakezelőt állítasz be, és naplózol.

Következő lecke: Üres, hibás és szélsőértékes bevitelek

A felhasználói bevitel ellenőrzése: üres mezők, hibás formátum, határértékek, és érthető visszajelzés (ErrorProvider, Validating).