Erindid ja erindihaldus CSharp programmeerimiskeeles

From ICO wiki
Jump to navigationJump to search

Antud õpiobjekti eesmärgiks on tutvustada erindite olemust ning nende kasutamist C# programmeerimiskeeles.

  • Mis on erind
  • Erindite kasutamine c# keeles
  • Erinevat tüüpi erindid
  • Erindi tõstmine

Mis on erind

Programmeerimise käigus ei ole võimalik kõiki ootamatuseid ette näha. Ikka tekib olukordi, kus rakendus või rakenduse kasutaja ei käitu ootuspäraselt. Nii on võimalik, et programmi täitmise käigus tekib viga ning rakendus hangub. Selleks, et selliseid olukordi vältida kasutatakse veahaldust ja erindeid.

Lihtsalt öeldes tähendab see seda, et meil on võimalik kirjutada programmilõik, mis reageerib vea tekkimisele ning käitub nii, nagu parajasti on ette öeldud (ehk tarvilik). Näiteks kui faili avada ei õnnestu, siis võime teatud juhtudel väljastada ainult sobiva veateate ning programmi töö katkestada, kuid samas võime ka avada dialoogiakna ning paluda kasutajal sobiv faili asukoht näidata. Kuidas olukord parajasti nõuab.

Hea komme on ka tekkivad vead logida, nii on võimalik hiljem analüüsida veaohtlike olukordi. Igast vea peab igal juhul jääma jälg, sest kui vea tagajärjel tekib andmete kadu või riknemine, siis peab olema võimalik hiljem kindlaks teha kus ja millal vastav olukord tekkis. Kuid üldiselt on veahalduse ülesanne just nimelt andmete riknemise vältimine ning võimalusel süsteemi kestev töövõime.


Erindite kasutamine c# keeles

Erindite püüdmiseks kasutatakse try{} catch{} plokke ning üldjoontes on nii, et try{} ploki sisse pannakse veaohtlik koodilõik ning catch{} plokis reageeritakse võimalikule veale.


Console.Write("Sisesta arv a: ");
int? a=null;
do
{
    try
    {
        a = Convert.ToInt32(Console.ReadLine()); //veaohtlik koht, toimub sisendandmete 
                                                                                    //töötlus
    }
    catch (Exception viga) 
    { //Siia jõutakse vaid siis, kui eelnevat tekkis viga
        Console.WriteLine("Tekkis viga: " + viga.Message);
        Console.Write("Sisesta arv a: ");
    }
} while (!a.HasValue);

|} Kusjuures tuleb jälgida seda, et väärtustüüpi muutujad, mis on loodud try{} ploki sees, ei ole väljast kättesaadavad. Seega kõik muutujad, mida on hiljem programmitäitmise käigus vaja, tuleb enne try{} plokki ette ära luua.

Lisalugemist: http://msdn.microsoft.com/en-us/library/system.exception(VS.85).aspx

Erinevat tüüpi erindid

(veebistuudiumi õppematerjalist)

Sama koodilõigu juures võib ette tulla mitmesuguseid probleeme. Kord ei leita sobivat andmefaili, teinekord ei saa teksti arvuks muundada ning mõnikord võib hoopis ette tulla jagamine nulliga. Vanemate programmeerimiskeelte juures oli tavaks iga käsu juures kontrollida, kas see õnnestus, ning siis püüda koheselt reageerida. Kui kohene parandamine on võimalik, on selline lähenemine hea. Kui aga peab parandamiseks palju asju ära muutma, siis kulub palju tööd. Selle lihtsustamiseks erindid ja veahaldus välja mõeldigi.

Ploki lõpus oleva catchi sulgudesse kirjutatakse selline erinditüüp, millele ollakse valmis reageerima. Nagu eespool oli - FormatException tekkis sisendandmete vormingu vea tõttu ning sellele probleemile ka reageeriti. Võib tekkida aga olukord, kus sisendiks on küll kõik numbrid, aga kokku tuleb int-vormingu jaoks liiga suur arv. Sellisel juhul heidetakse hoopis OwerflowException. Eraldi catchidega püüdes saab nendele vigadele sobivalt reageerida.

Vigade klassid moodustavad isekeskis hierarhia. Selle puu juureks on klass nimega Exception. Tema alamklassideks on hulk .NET runtime käivitamisega seotud probleemiklasse, aga muuhulgas ka SystemException, mille alt siis omakorda kättesaadavad enamik meil programmides ettetulevaid System-nimeruumi objektidega seotud erindeid.

SystemExceptioni enese alt leiab omakorda näiteks ArithmeticExceptioni, mille juurest omakorda OverflowExceptioni ja DivideByZeroExceptioni. Kui me tahame liialt suurt arvu (ületäitumine) ning nulliga jagamist kontrollida sama catchi sees, siis võib piirduda ArithmeticExceptioni püüdmisega. Kui aga kummagi olukorra jaoks on soov käivitada eri kood, siis tasub need eraldi kinni püüda.

Viga jääb kinni ainult ühes catch-plokis. Seepärast pannakse detailsemad erindid püüdmisel ettepoole ning üldisemad tahapoole. Muidu juhtuks, et teade jääb üldisematele tingimustele vastavasse plokki kinni ja väljavalitud lõiku kunagi ei pruugitagi. Tahtes kõik teated kindlasti kätte saada, võib lõppu panna catch(Exception). Sellele tüübile vastavad kõik veateated - ka need, mis on kõigist muudest püünistest juba mööda tulnud.

Juhul, kui soovitakse saabunud veateate andmetega midagi lähemat ette võtta, saab selle püüda omaette muutujasse ning sealtkaudu erindiobjektiga suhelda.

catch(FormatException probleem)
{
   Console.WriteLine("Viga sisendandmetega: "+probleem.Message);
}

Kui aga piisab vaid teadmisest, et juhtus vastavat tüüpi olukord ning sellest teadmisest on meile reageerimiseks küllalt, siis võib oma muutuja loomata jätta.


catch(OverflowException)
{
    Console.WriteLine("Liiga suur arv.");
}

finally-plokki püüniste lõpus kasutatakse käskluste jaoks, mis tulevad igal juhul ära teha. Näiteks faili sulgemine andmete lugemisel: isegi siis, kui lugemine ebaõnnestus, tuleb fail teistele kasutajatele kättesaadavaks teha. Aga mainimist väärib, et finally-plokki jõutakse siiski ainult juhul, kui viga polnud või sai sellele reageeritud. Nii et kindlaks lõpuni jõudmiseks on mõistlik panna viimaseks veapüüniseks ikkagi catch(Exception). Kuigi - vahel soovitatakse, et pigem jäta erind püüdmata, kui et püüad midagi, millega sa mõistlikku peale hakata ei oska. Et kui tuleb ametlik veateade, on see vahel parem, kui omalt poolt vea peitmine, mis võib hiljem vigaste andmete näol kusagil kätte maksta.

Nüüd aga näide tervikuna. Käsurea parameetrina oodatakse numbreid, mille programm arvuks teisendab ning välja trükib. Juhtub aga midagi sobimatut, siis teatatakse vastav veateade.

using System;
class Erind3{
   public static void Main(string[] arg){
     try{
        if(arg.Length!=1){
          Console.WriteLine("Kasuta kujul: Erind3 sisendarv");
          return;
        }
        string tekst1=arg[0];
        int arv1=int.Parse(tekst1);
        Console.WriteLine("Sisestati edukalt "+arv1);
     } catch(FormatException probleem){
        Console.WriteLine("Viga sisendandmetega: "+probleem.Message);
     } catch(OverflowException){
        Console.WriteLine("Liiga suur arv.");
     } catch(Exception){
        Console.WriteLine("Tundmatu probleem");
     } finally{
        Console.WriteLine("Plokk otsas");
     }     
   }
}
/*
C:\Projects\oma\naited>Erind3
Kasuta kujul: Erind3 sisendarv
Plokk otsas
C:\Projects\oma\naited>Erind3 tere
Viga sisendandmetega: Input string was not in a correct format.
Plokk otsas
C:\Projects\oma\naited>Erind3 1234567890123456
Liiga suur arv.
Plokk otsas
C:\Projects\oma\naited>Erind3 78
Sisestati edukalt 78
Plokk otsas
*/

Erindi tõstmine

Teatud juhtudel on nii, et viga ei teki süsteemselt, kuid programmeerijal on soov tekitada ise üks uus viga ning kutsuda nii esile erindi lahendamine.

See võib vajalik olla näiteks siis, kui programmi loogika kohaselt on tegemist veaga, kuid samas .Net sellist viga ei tunne.

Console.Write("Sisesta kolmnurga pikim  külg: ");
int? a=null;
do
{
    try
    {
        a = Convert.ToInt32(Console.ReadLine());
    }
    catch (Exception viga)
    {
        Console.WriteLine("Tekkis viga: " + viga.Message);
        Console.Write("Sisesta kolmnurga pikim  külg: ");
    }
} while (!a.HasValue);
Console.Write("Sisesta kolmnurga lühim  külg: ");
int? b = null;
do
{
    try
    {
        b = Convert.ToInt32(Console.ReadLine());
    }
    catch (Exception viga)
    {
        Console.WriteLine("Tekkis viga: " + viga.Message);
        Console.Write("Sisesta kolmnurga lühim  külg: ");
    }
} while (!b.HasValue);
if (b>a)
    throw new Exception("Kasutaja ei kuula sõna");

Tõstetav erind ei pruugi olla Exception tüüpi, vaid võib olla mistahes erindi tüüpi, võimalikud tüübid on näiteks:


SystemException Käivitamisel tekkivad vead
AccessException Ligipääsuvead (eelkõige meetoditele ja andmeväljadele)
ArgumentException Argumendi mittesobivused
ArgumentNullException Null viide anti edasi kuhugi, kus see ei olnud aksepteeritav
ArgumentOutOfRangeException Skoobivead
ArithmeticException Aritmeetikavead
ArrayTypeMismatchException Massiivi mittesobivat tüüpi liikme lisamise viga
BadImageFormatException Pildi formaadi viga
CoreException Runtime poolt tõstetud vead
DivideByZeroException Nulliga jagamise viga
FormatException vorminguviga
IndexOutOfRangeException Skoobivead
InvalidCastExpression Vormidamise käigus tekkivad vead
MissingMemberException Mittesobiva teegi põhjustatud vead
NotFiniteNumberException Mittesobiva numbri vead
NotSupportedException Viga, mis näitab, et vajalik meetod on implementeerimata
OutOfMemoryException Mälu ületäitumise viga
StackOverflowException Puhvri ületäitumise viga

Sellist konstruktsiooni tuleks muidugi kasutada äärmise vajaduse korral, sest erindite tõstmine ning lahendamine on väga ressursimahukas tegevus.