Šodien Vakar Andrejs jau aizskāra tēmu, par kuru es jau pasen gribu uzrakstīt, bet kaut kā nav sanācis. Tātad, izņēmuma situāciju (exception) lietošana. Lieta par kuru daudzi diskutē, daudzi lieto, bet kā jau redzējām, ne vienmēr tā tiek pielietota korekti.
Izņēmuma situācijas kodā tiek apstrādātas ar try...catch bloka palīdzību, kura sintakse ir apmēram šāda (precīzi var apskatīties msdn):
try
{
//do something
}
catch (Exception ex)
{
//do handling
}
Kas notiek pie šāda koda izpildes? Pavisam vienkārši - ja kāda no darbībām try blokā rada izņēmuma situāciju ("izmet eksepciju"), tad vadība tiek nodota catch blokam, kurš tad arī mēģina veikt izņēmuma situācijas apstrādi. Jāsaprot, ka ne velti tās tiek sauktas par izņēmuma situācijām, t.i. situācijām kuras kāds kods nespēj korekti apstrādāt. Šajā saistībā arī nodefinēsim pirmo noteikumu darbā ar izņēmuma situācijām:
1. noteikums.
Jāapstrādā ir tikai tās izņēmuma situācijas kuras ir iespējams apstrādāt.
Tas nozīmē, to, ka ir "jāķer" pēc iespējas specifiskāka eksepcija, un tā arī jācenšās apstrādāt. Neviens no mums nav Dievs (ok, es domāju, ka Skots šo nelasīs), un nevar zināt visas izņēmumu situācijas, kuras var rasties. Vienīgie gadījumi, kur, manuprāt, var izmantot vispārīgo izņēmuma situāciju apstrādi ir tad, ja jāveic kāda darbība neatkarīgi no izņēmuma situācijas tipa (piem., jāsūta e-pasts administratoram).
Pie izņēmuma situāciju apstrādes bieži vien mēdz būt tā, ka tiek veiktas kādas darbības un pēc tam eksepcija "padota uz augšu". Šim nolūkam izmantot atslēgvārdu throw:
throw new ArgumentOutOfRangeException();
Detalizētāku aprakstu, diemžēl, nāksies lasīt MSDN.
Izņēmuma situāciju radīšana ("eksepciju mešana") arī bieži tiek izmantota nepareizi, tāpēc vajadzētu ievērot nākošos noteikumu:
2. noteikums.
Nekad "nemest" vispārīgo eksepcijas tipu Exception.
Šis noteikums lieliski saskan ar pirmo noteikumu. Tātad, ja mēs apstrādajam tikai specifiskas izņēmuma situācijas ar kurām protam tikt galā, tad ir loģiski, ka problēmas gadījumā arī tiek pateikts, kas tad īsti ir par problēmu. Ikdienā vajadzētu pietikt ar .NET platformas piedāvātajiem izņēmumu situāciju tipiem, bet pēc vēlēšanās un vajadzības var izveidot arī savus.
Šie divi tad būtu pamatnoteikumi. Pirmais par izņēmuma situāciju apstrādi, otrais par radīšanu. Trešais padoms (noteikums) ir par abām lietām kopā.
3. noteikums.
Saglabāt izsaukumu vēsturi ("call stack")
Lai paskaidrotu šo noteikumu, izmantosim piemēru:
try
{
CallMethodWhichFails();
}
catch (Exception ex)
{
//Do handling;
throw (ex);
}
Pirmajā acu uzmetienā liekas, ka viss ir kārtībā. Notiek izņēmuma situācijas apstrāde, pēc tam eksepcija tiek padota tālākai apstrādei augstāka līmeņa kodam. Labi, varbūt nav ievērots pirmais noteikums (tā kā nezinam, kas notiek apstrādē, tad droši to apgalvot nevar). Tomēr šādai pieejai ir viens milzīgs mīnuss - šādā gadījumā tiek pazaudēta visa izsaukumu vēsture ("call stack") un atkļūdošanas procesa gaitā nav iespējams noteikt, kurā vietā izņēmuma situācija ir radusies. Pareizi būtu lietot kādu no konstrukcijām:
throw;
vai:
throw new Exception("Description", ex);
Abos gadījumos izsaukumu vēsture lielākā vai mazākā mērā tiek saglabāta. It kā vienkārša lieta, bet pieredze liecina, ka pat pieredzējuši programmētāji to reizēm nezin.
Tas šodienai arī būtu viss. Vēl tikai jāpiebilst, ka nav vērts rakstīt try...catch bloku, ja vienīgais mērķis ir padot eksepciju tālāk. .NET platforma pati lieliski tiek ar šo uzdevumu galā.
Literatūras saraksts:
- 1. FAQ: Why does FxCop warn against catch(Exception)? - Part 1 [Nick Guerrera]
- 2. Common Exception Types [Brad Abrams]
- 3. Design Guidelines Update: Exception Throwing [Krzysztof Cwalina]
- 4. Best Practices for Handling Exceptions
Varat mani apsveikt, pirmais maģistrantūras eksāmens (angļu val.) ir nokārtots, pēc apmēram astoņām stundām ir nākošais. Rīt pamēģināšu atrast laiku, lai nopublicētu atbildi Mihaila komentāriem, so, stay tuned...