You are on page 1of 19

Upravljanje greškama i izuzecima

U ovom poglavlju proučavaće se sledeće celine:

- Uvođenje pojmova grešaka i izuzetaka


- Korišćenje višestrukih TRY blokova
- Korišćenje višestrukih CATCH blokova
- “Izbacivanje” (“THROWING”) izuzetaka
- Ugnježđivanje TRY blokova
- Kreiranje sopstvenih izuzetaka
- Korišćenje označenog / neoznačenog operatora

Uvođenje pojmova grešaka i izuzetaka


Greške su neispravnosti koje se generišu tokom pisanja programa i koje se moraju otkloniti kako
bi se omogućilo kompajliranje i izvršavanje tog programa. Uopšteno govoreći, greške se mogu
podeliti na sledeće tipove:
- Sintaksne greške: takođe poznate kao greške u toku kompajliranja, sintaksne greške se javljaju
kada se neki programski kod ne poklapa sa gramatikom korišćenog programskog jezika. Na
primer, možete otkucati rezervisane reči velikim slovima (C# razlikuje velika i mala slova),
izostaviti tačku i zarez (;) ili koristiti promenljivu koja još nije deklarisana. Ugrađeni uređivač
koda u “Microsoft Visual Studio”-u pomaže vam da izbegnete sintaksne greške. Dok kucate kod,
IntelliSense počinje da pravi listu reči koje odgovaraju unetom znaku i prikazuje predloge za
izbor. Takođe, podvlači pogrešno otkucanu ključnu reč da ukaže na mogućnost greške.
- Logičke greške: Logičke greške se javljaju kada program nema sintaksne greške, izvršava se i
daje izlazne podatke, ali su oni pogrešni ili nepredvidivi. Osim što daju pogrešne rezultate,
logičke greške mogu dovesti do blokade računara ili pada sistema. Uklanjanje logičkih grešaka
zahteva detaljnu analizu koda i primenu odgovarajućih koraka za otklanjanje grešaka.
Logičke greške se pojavljuju nakon što pokrenete program. Pored logičkih grešaka, postoji i
jedan dodatni tip greške u toku rada, poznate kao izuzetak. “Izuzetak” je nepredviđena greška
koja se u programu povremeno pojavljuje, odnosno kada se za to steknu specifični uslovi. Dobar
program treba da sadrži kod za otkrivanje i obradu takve greške.
Rukovanje izuzecima je mehanizam koji rukuje greškama tokom izvršavanja. Oslobađa vas od
ručnog proveravanja koda, jer kad god se dogodi izuzetak aktivira se sistemski rukovalac
izuzetkom koji izvršava adekvatan sigurnosni kod. Drugim rečima, izuzeci vam pomažu da
izbegnete rušenje aplikacije i prikažete razloge za grešku u razumljivom formatu.
Napomena
Ako ne rukujete izuzecima, .NET runtime okruženje obezbeđuje podrazumevani mehanizam koji
prekida program. Slede neki uobičajeni razlozi za izuzetke:
- Greške u kodu: Ove greške uključuju izostavljanje provere deljenja nulom, prosleđivanje
nevažećih argumenata metodu i primenu konverzija na pogrešne tipove.
- Greška u toku izvršavanja: Ove greške uključuju nedostatak radne memorije ili memorije
steka, grešku u pristupu datotekama, nedostupnost konfiguracione datoteke, neuspeh
povezivanja sa bazom podataka ili neuspeh aktiviranje neke od spoljašnjih komponenti.

Hijerarhija izuzetaka
U C#-u izuzeci su predstavljeni klasama. Sve klase izuzetaka su izvedene iz ugrađene klase
izuzetaka Exception, koja je deo System imenskog prostora. Dakle, svi izuzeci su podklase
Exception-a.
Postoje dve vrste izuzetaka:
- ApplicationException: Ove izuzetke generiše korisnički program
- SystemException: Ove izuzetke generiše “Common Language Runtime” (CLR).

System.Exception je bazična klasa standardne hijerarhije izuzetaka. To je mesto gde C#


“runtime” sistem generiše sve izuzetke.
Evo nekoliko standardnih izuzetaka:
- System.ArithmeticException: Osnovna klasa za izuzetke koji se javljaju tokom aritmetičkih
operacija, kao što su System.DivideByZeroException i System.OverflowException
- System.DivideByZeroException: Pojavljuje se kada se ceo broj podeli sa 0
- System.OverflowException: Pojavljuje se kada dođe do prekoračenja maksimalne dozvoljene
vrednosti
- System.ArgumentException: Pojavljuje se kada je bilo koji argument metode nevažeći
- System.ArgumentNullException: Pojavljuje se kada se nekoj metodi prosleđuje null argument
- System.ArgumentOutOfRangeException: Pojavljuje se kada je vrednost argumenta van
zadatog opsega
- System.ArrayTypeMismatchException: Pojavljuje se kada element nekompatibilnog tipa
podataka pokuša da se uskladišti u nizu
- System.IndexOutOfRangeException: Pojavljuje se kada se pokuša indeksiranje niza pomoću
indeksa koji je ili manji od 0 ili izvan njegovih granica
- System.InvalidCastException: Pojavljuje se kada ne uspe eksplicitna konverzija iz osnovnog
tipa ili interfejsa u izvedeni tip
- System. NullReferenceException: Pojavljuje se kada se koristi nulta referenca (kada se poziva
na nepostojeći objekat)
- System.OutOfMemoryException: Pojavljuje se kada raspoloživa memorija postane premala da
bi se zadovoljio zahtev za dodelu memorije
- System.StackOverflowException: Pojavljuje se kada je izvršni stek prepunjen

Ključne reči koje se koriste u rukovanju izuzecima

U programskom jeziku C# rukovanje (upravljanje) izuzecima se odvija pomoću četiri ključne


reči: try, catch, throw i finally.
- try: Kod u kome želite da proverite da li se pojavi bilo kakav izuzetak je zatvoren u try bloku.
Ako dođe do izuzetka unutar ovog bloka, aktivira se procedura za taj izuzetak.
- catch: Obrada izuzetka koji se javlja prilikom pojave izuzetka u try bloku se aktivira (“hvata”)
preko koda navedenog u catch bloku. Drugim rečima, catch obezbeđuje kod za rukovanje
odgovarajućim izuzecima. Try blok može da izbaci više izuzetaka za čiju obradu možete koristiti
više catch blokova.
- throw: C# izvršni proces (“runtime”) automatski izbacuje izuzetke, ali ponekad imate potrebu
da izuzetak aktivirate nezavisno i samostalno. Throw blok se koristi za ručno aktiviranje
(“izbacivanje”) izuzetka.
- finally: Kada se pojavi izuzetak program se obično prekida i iz njega se na neki način izlazi.
Programski kod koji želite uvek da izvodite, bez obzira da li se izuzetak dogodio ili ne, napisan je
unutar finally bloka. Iz finally bloka ne možete “izaći” korišćenjem break, continue, return ili
goto naredbi.
Ako dođe do bilo kakvog izuzetka unutar try bloka, kontrola se prenosi na odgovarajući catch
blok i kasnije na finally blok. I catch i finally blokovi su opcioni. Blok try može postojati sa
jednim ili više catch blokova i/ili finally blokom. Ako nema izuzetka unutar bloka try, kontrola se
direktno prenosi u finally blok.
Napomena
Try i catch blokovi rade zajedno. Ne može postojati catch bez try.
Evo sintakse blokova try-catch-finally:
Try
{
// Programski kod koji može izavati izuzetak
}

catch(Excep_Type1 Excep_Obj)
{
// Programski kod koji obrađuje pojavu jednog tip izuzetka
}

catch(Excep_Type2 Excep_Obj)
{
// Programski kod koji obrađuje pojavu drugog tip izuzetka

}
:::::::::::::::::::::::::::::::::::

[Finally
{
// Programski kod koji se uvek izvršava
} ]

Programski kod koji može da izazove izuzetak je zatvoren u try bloku. Kada dođe do izuzetka
Excep_Type1, on se pojavljuje (“izbacuje”= “throw”) i aktivira (“hvata”=”catch”) odgovarajući
catch blok, koji izvršava akciju obrade detektovanog problema. Može biti više od jednog catch
bloka povezanog sa try blokom i svaki obrađuje po jednu vrstu izuzetka. Ako se dogodi izuzetak
za koji ne postoji catch blok, .NET runtime okruženje izvršava svoj podrazumevani mehanizam
za rukovanje izuzetkom: prekida izvršavanje programa. Drugim rečima, programski kod
definisan u catch bloku se izvršava, a svi ostali catch blokovi se zaobilaze. Kada je izuzetak
uhvaćen, objekat izuzetka Excep_Obj prima svoje informacije. Ako se u catch bloku ne koristi
Except_Obj, onda se u tom bloku on ne mora ni navoditi.
Ako se ne pojavi nikakav izuzetak, try blok se izvršava u potpunosti, svi njegovi catch blokovi se
zaobilaze i program nastavlja sa izvršavanjem od finally bloka (ako je tamo). Finally blok sadrži
programski kod koji se izvršava bez obzira da li dođe do izuzetka ili ne.
Napomena
Catch blok se izvršava samo ako se pojavi izuzetak. Da bi razumeli rukovanje izuzecima i njegovu
praktičnu upotrebu, napisaćemo program koji pravi greške, ali ne primenjuje rukovanje
izuzecima.

using System;
class TryCatch1
{
static void Main()
{
int x = 10, y = 0, z;
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
}
}

Prethodni program se bez greške kompajlira, ali pokazuje grešku tokom izvršavanja. Deljenje sa
nulom je greška u toku rada. Pošto u ovom kodu nije implementirano rukovanje izuzetkom,
program se završava neregularno sa sledećom porukom o grešci:

Unhandled Exception: System.DivideByZeroException: Attempted to divide by


zero. At TryCatch1.Main()

Podsetimo se da ako se bilo koji izuzetak ne “uhvati” kroz odgovarajući catch blok,
podrazumevani mehanizam .NET runtime-a prekida izvršenje celog programa.
Ako korisnik koristi Windows, izuzetak prikazuje ekran sa greškom u operativnom sistemu
Windows. Program prikazuje izuzetak u prozoru konzole, ali Windows pokušava da pošalje
problem Microsoft-u da ga reši.
Prethodni program sada će biti modifikovati tako što će se dodati kod koji bi mogao da stvori
grešku tokom izvođenja unutar try bloka. Iz ovoga se takođe vidi i kako se rukuje greškom
deljenja sa nulom tako što se uhvati izuzetak DivideByZeroException. Prikazan je program sa try
i catch blokovima:

using System;
class TryCatch1
{
static void Main()
{
int x = 10, y = 0, z = 0;
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}

Izlaz:
Pojavio se izuzetak.

Pošto se obradi izuzetak DivideByZeroException, program se neće neočekivano prekinuti kao


ranije. Umesto toga, nakon izuzetka, kontrola programa skače sa try bloka na catch blok
(završavajući try blok između) koji definiše odgovarajući izuzetak, DivideByZeroException.
Nakon rukovanja izuzetkom (izvršavanje naredbi unutar catch bloka), program nastavlja svoje
izvršavanje nakon catch bloka. Ako je prisutan finally blok, izvršava se i kod unutar finally bloka.
Zbog toga rukovanje izuzetkom omogućava vašem programu da odgovori na greške u toku
izvođenja i nastavi sa izvršavanjem koda.
Beleška
Ako try blok ne izazove izuzetak, neće biti izvršen nijedan catch, a program će nastaviti
izvršavanja dela nakon catch bloka.
Šta ako program ima greške na dva ili više mesta? Može li se imate više od jednog try bloka u
programu koji rukuje izuzecima u različitim delovima programa? Da, može i to će biti
objašnjeno u narednoj celini.

Korišćenje višestrukih try blokova


Za proveru različitih delova programu može postojati više try blokova. U narednom programu u
toku izvršavanja proveravaju se dva bloka kodova za greške tako što se zatvoraju u dva različita
try bloka i kroz catch blokove obrađuju se njihovi odgovarajući izuzeci.

using System;

class TryCatch1
{
static void Main()
{
int x = 10, y = 0, z = 0;
int[] p = new int[3];
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak - deljenje nulom.", ex);
}
try
{
p[3] = 23;
Console.WriteLine("Element niza je {0}", p[0]);
}
catch (System.IndexOutOfRangeException ex)
{
Console.WriteLine("Pojavio se izuzetak - indeks van dozvoljenog opsega.", ex);
}
finally
{
Console.WriteLine("Ovo je finalni blok.");
}
Console.ReadKey();
}
}
Izlaz:
Pojavio se izuzetak- deljenje nulom.
Pojavio se izuzetak- indeks van dozvoljenog opsega.
Ovo je finalni blok.

U prethodnom programu proveravaju se sledeće dve greške:


- da li je bilo koji broj podeljen sa 0
- da li je vrednost indeksa koju koristi niz unutar propisanog opsega
Pošto je promenljiva x podeljena sa y, što je 0, dolazi do izuzetka DivideByZeroException.
Program skače sa try bloka na catch blok i prikazuje poruku Pojavio se izuzetak - deljenje sa
nulom. Nakon izvršenja catch bloka, program nastavlja sa svojim izvršavanjem prateći catch
blok. Takođe, int niz p je definisan kao niz sa 3 elementa. Prema tome, važeće vrednosti indeksa
niza p su 0, 1 i 2. Ako pristupite bilo kom indeksu u opsegu od 0 do 2, program se pokreće
uspešno, ali ako pokušate da indeksirate niz izvan njegovih granica, kao što je pokušaj da
koristite vrednost indeksa 3 ili više na vašem nizu p, izbacuje se izuzetak
IndexOutOfRangeException. Prethodni program izbacuje izuzetak IndexOutOfRangeException,
koji je “hvatan” catch blokom, čime se na ekranu prikazuje poruka Pojavio se izuzetak- indeks
van dozvoljenog opsega. Bez obzira da li se dogodi izuzetak ili ne, kod u finally bloku se uvek
izvršava. Tako program na ekranu takođe prikazuje poruku Ovo je finalni blok. Ako ne rukujete
izuzetkom IndexOutOfRangeException, program se prekida, prikazujući sledeću poruku o
grešci:
Unhandled Exception: System. IndexOutOfRangeException: Index was outside the
bounds of the array. At TryCatch1.Main()
U programu prikazanom u sledećem programskom listingu, prikazano je kako se “hvata” još
jedan izuzetak: FormatException.
Ako se konvertuje string u ceo broj pomoću funkcije Convert.ToInt32. niz se može pretvoriti u
ceo broj samo kada sadrži numerički sadržaj. Ako se pokuša konverzija nenumeričkog sadržaja
u ceo broj, “izbacuje” se izuzetak FormatException.

using System;
class TryCatch1
{
static void Main()
{
int x = 10, y = 0, z = 0, n = 0;
int[] p = new int[3];
String str;
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak - deljenje nulom.", ex);
}
try
{
p[3] = 23;
Console.WriteLine("Element niza je {0}", p[0]);
}
catch (System.IndexOutOfRangeException ex)
{
Console.WriteLine("Pojavio se izuzetak - indeks van dozvoljenog opsega.", ex);
}
try
{
str = "Marko";
n = Convert.ToInt32(str);
Console.WriteLine("Vrednost u promenljivoj n je {0}", n);
}
catch (FormatException ex)
{
Console.WriteLine("Pojavio se izuzetak - greska u formatu stringa." ex);
}
finally
{
Console.WriteLine("Ovo je finalni blok.");
}
Console.ReadKey();
}
}

Izlaz:
Pojavio se izuzetak- deljenje nulom.
Pojavio se izuzetak- indeks van dozvoljenog opsega.
Pojavio se izuzetak- greska u formatu stringa.
Ovo je finalni blok.

U delovima programa označenih sa DivideByZeroException i IndexOutOfRangeException,


pojavljuju se izuzeci uhvaćeni njihovim odgovarajućim catch blokovima. Catch blokovi prikazuju
poruke Pojavio se izuzetak- deljenje sa nulom. i Pojavio se izuzetak- indeks van dozvoljenog
opsega. Takođe, pošto program pokušava da konvertuje niz Marko u ceo broj pomoću funkcije
Convert.ToInt32, što nije moguće, pojavljuje se izuzetak FormatException. Izuzetak
FormatException je “uhvaćen” od strane odgovarajućeg catch bloka, pa se na ekranu prikazuje
poruka Pojavio se izuzetak- greska u formatu stringa. U poslednjem redu, finally blok se
izvršava i prikazuje poruku Ovo je finalni blok.
Ne samo da program može imati više try blokova, već i jedan try blok može imati više catch
blokova.
Korišćenje višestrukih catch blokova
Moguće je povezati više catch blokova sa jednim try blokom. Svaki catch blok mora “uhvatiti”
drugačiji tip izuzetka. Ceo kod za koji se očekuje da sadrži grešku je zatvoren u try bloku. Kada
bilo koja komanda u try bloku izazove izuzetak, on se “hvata” i zadržava od strane
odgovarajućeg catch bloka. Ako bilo koji catch blok obradi izuzetak, izvršava se samo kod u tom
catch bloku, a program nastavlja sa izvršavanjem nakon catch bloka. Sledeći program “hvata” tri
izuzetka: granice niza, greške deljenja nulom i format.

using System;
class TryCatch2
{
static void Main()
{
int x = 10, y = 0, z = 0, n = 0;
int[] p = new int[3];
String str;
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
p[3] = 23;
Console.WriteLine("Element niza je {0}", p[0]);
str = "Marko";
n = Convert.ToInt32(str);
Console.WriteLine("Vrednost u promenljivoj n je {0}", n);
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak - deljenje nulom.", ex);
}
catch (System.IndexOutOfRangeException ex)
{
Console.WriteLine("Pojavio se izuzetak - indeks van dozvoljenog opsega.", ex);
}
catch (FormatException ex)
{
Console.WriteLine("Pojavio se izuzetak - greska u formatu stringa." , ex);
}
Console.ReadKey();
}
}

Izlaz:
Pojavio se izuzetak - deljenje nulom.

Promenljiva x je podeljena sa y, što je 0, pa se izbacuje izuzetak DivideByZeroException koji se


“hvata” odgovarajućim catch blokom. Poruka Pojavio se izuzetak - deljenje nulom. prikazana je
tako što izuzetak rukuje odgovarajućim catch blokom. Ostatak naredbi u try bloku koji prate
naredbu koja je izazvala izuzetak DivideByZeroException se uopšte ne izvršavaju.
Ako je potrebno “uhvatiti” sve tipove izuzetaka, definiše se catch blok bez tipa izuzetka. Ovo
dovodi do toga da se catch blok izvršava bez obzira na vrstu izuzetka. Sledeća sintaksa obrađuje
sve izuzetke koji se mogu pojaviti u bloku try.

try
{
// Programski kod koji može izavati neki izuzetak
}
catch
{
// Programski kod koji obrađuje pojavu svakog izuzetka
}

Iako možete da koristite catch blok sa parametrom tipa Exception da uhvatite sve izuzetke koji
se mogu pojaviti u try bloku, svi izuzeci se direktno ili indirektno nasleđuju iz klase Exception.

Sledeći program pokazuje catch blok koji može da obradi ili “hvati” sve vrste izuzetaka. Program
sadrži try blok sa kodom koji izbacuje izuzetke DivideByZeroException,
IndexOutOfRangeException, odnosno FormatException. Tri izuzetka su uhvaćena sa tri catch
bloka, gde je svakom catch bloku omogućeno da uhvati sve izuzetke.

using System;
class CatchAll
{
static void Main()
{
int x = 10, y = 0, z = 0, n = 0;
int[] p = new int[3];
String str;
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
}
catch (Exception ex)
{
Console.WriteLine("Pojavio se izuzetak - " + ex.ToString());
}
try
{
p[3] = 23;
Console.WriteLine("Element niza je {0}", p[0]);
}
catch (Exception ex)
{
Console.WriteLine("Pojavio se izuzetak - " + ex.ToString());
}
try
{
str = "Marko";
n = Convert.ToInt32(str);
Console.WriteLine("Vrednost u promenljivoj n je {0}", n);
}
catch (Exception ex)
{
Console.WriteLine("Pojavio se izuzetak - " + ex.ToString());
}
Console.ReadKey();
}
}

Izlaz:
Pojavio se izuzetak - System.DivideByZeroException: Attempted to divide by zero.
at CatchAll.Main()
Pojavio se izuzetak - System.IndexOutOfRangeException: Index was outside the bounds
of the array.
at CatchAll.Main()
Pojavio se izuzetak - System.FormatException: Input string was not in a correct
format.
at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer&
number, NumberFormatInfo info, Boolean parseDecimal)
at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at CatchAll.Main()

Vidi se da bilo koji tip izuzetka u try bloku može biti “uhvaćen” nekim od catch blokova, a svaki
catch blok sadrži parametar tipa izuzetka. Pošto svi izuzeci nasleđuju klasu Exception, svaki
catch blok može da obradi bilo koji izuzetak.
Izbacivanje (“throw”) izuzetka
Do sada ste “hvatali” izuzetke koje automatski generiše runtime sistem. Takođe možete ručno
da izbacite izuzetak koristeći naredbu throw. Sintaksa: throw exceptObj; Ovde exceptObj mora
biti instanca klase koja je izvedena iz klase Exception. Sledeći program pokazuje kako se izuzetak
DivideByZeroException izbacuje ručno.

using System;
class ThrowException
{
static void Main()
{
int x = 10, y = 5, z = 0;
try
{
z = x / y;
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
throw new DivideByZeroException();
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak." , ex);
}
Console.ReadKey();
}
}

Izlaz:
Rezultat deljenja 10 sa 5 je 2
Pojavio se izuzetak.

Možete videti da je DivideByZeroException kreiran pomoću operatora new u naredbi throw.


Operator new kreira novi objekat. Ali zašto stvarate objekat? To je jednostavno. Throw blok
“izbacuje” objekat, tako da morate da kreirate objekat da biste ga “izbacilli”. Izuzetak koji je
uhvatio jedan catch blok može se ponovo “baciti” tako da ga može uhvatiti drugi catch blok.
Ideja je da se proveri da li odgovarajući obrađivači izuzetaka pravilno rukuju izuzecima. Da biste
ponovo izbacili isti izuzetak, jednostavno napišite throw bez navođenja imena izuzetka u catch
bloku.
Programski kod prikazan u sledećem listingu pokazuje kako je ručno “izbačen” izuzetak
“uhvaćen” catch blokom i kako catch blok ponovo izbacuje drugi izuzetak koji je zauzvrat hvatan
sledećim catch blokom. Izuzetak DivideByZeroException se izbacuje korišćenjem naredbe
throw u try bloku. DivideByZeroException je hvatan blokom catch koji, nakon što prikaže
željenu poruku, izbacuje drugi izuzetak: IndexOutOfRangeException.
IndexOutOfRangeException je uhvaćen catch blokom definisanim u glavnom metodu.

using System;
class ThrowException
{
static void disp(string str)
{
int x = 10, y = 5, z = 0;
try
{
z = x / y;
Console.WriteLine("Zdravo {0}!", str);
Console.WriteLine("Rezultat deljenja {0} sa {1} je {2}", x, y, z);
throw new DivideByZeroException();
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak - deljenje nulom ", ex);
throw new IndexOutOfRangeException("Izuzetak izbacen disp metodom ",
ex);
}
}
static void Main()
{
try
{
disp("Marko");
}
catch (System.IndexOutOfRangeException ex)
{
Console.WriteLine("Izuzetak se pojavio u Main bloku – indeks van opsega.Poruka: {0} ", ex.Message);
Console.WriteLine("Unutrasnji izuzetak je {0}", ex.InnerException);
}
Console.ReadKey();
}
}

Izlaz:
Zdravo Marko!
Rezultat deljenja 10 sa 5 je 2
Pojavio se izuzetak - deljenje nulom
Izuzetak se pojavio u Main bloku – indeks van opsega. Poruka: Izuzetak izbacen disp metodom
Unutrasnji izuzetak je System.DivideByZeroException: Attempted to divide by zero.
at ThrowException.disp(String str)

Možete videti da se metoda disp poziva iz try bloka. String Marko se prosleđuje dok se poziva
disp metoda. U disp metodi prikazujese poruka Zdravo Džone! Takođe, DivideByZeroException
se izbacuje pomoću naredbe throw unutar try bloka. DivideByZeroException je “uhvaćen”
catch blokom koji na ekranu prikazuje poruku Pojavio se izuzetak - deljenje nulom. U catch
bloku, izbacuje se još jedan izuzetak, IndexOutOfRangeException, koji se nakon toga hvata
catch blokom definisanim u glavnom metodu. Blok catch u glavnom metodu prikazuje na ekranu
poruku Izuzetak se pojavio u Main bloku – indeks van opsega. Za prikaz poruka iz svih
unutrašnjih izuzetaka generisanog izuzetka koristite svojstvo InnerException. Svojstvo
InnerException sadrži referencu na unutrašnje izuzetke (istoriju svih izuzetaka koji su izbačeni).
Ugnežđivanje blokova pokušaja
Možete ugnjezditi jedan try blok u drugi. Ako je izuzetak generisan unutar unutrašnjeg bloka
pokušaja i nije uhvaćen od strane njegovog(ih) bloka(-ova), on prelazi na spoljašnji try blok.
Sintaksa ugnežđenih try blokova:

try // Outer try block


{
...............
...............
try // Inner try block
{
// Code that can cause exception(s)
}
catch(Excep_Type1 Excep_Obj) // Inner catch block
{
// Code to handle the exception
}
[finally // Inner finally block
{
// Any cleanup code
} ]
}
catch(Excep_Type1 Excep_Obj) // Outer catch block
{
// Code to handle the exception
}
[finally // Outer finally block
{
// Any cleanup code
} ]

Sledeće okolnosti mogu da se jave dok se koriste ugnežđeni try blokovi:


- Ako je izuzetak izbačen unutar spoljašnjeg try bloka, ali izvan unutrašnjeg try bloka, izuzetak je
uhvaćen od strane spoljašnjeg catch bloka i, ako postoji, izvršava se spoljašnji finally blok.
- Ako je izuzetak izbačen u unutrašnjem try bloku, hvata ga unutrašnji catch blok i izvršava se
finally blok (ako postoji). Izvršenje se nastavlja unutar spoljašnjeg try bloka iz naredbe koja sledi
iza catch bloka.
- Ako se u unutrašnjem try bloku izbaci izuzetak i ne postoji odgovarajući catch blok koji bi njime
upravljao, izvršava se unutrašnji finally blok (ako postoji). Takođe, spoljašnjii catch blok za
hvatanje se pretražuju da bi se videlo da li on može da obradi taj izuzetak. Ako je ta mogućnost
dostupna, spoljašnji catch blok obrađuje taj izuzetak, a nakon toga se izvršava spoljašnji finally
blok, naravno ako postoji.
Program prikazan u sledećem listingu pokazuje ugnežđene try blokove. Unutrašnji try blok
izbacuje IndexOutOfRangeException koji je uhvaćen od strane unutrašnjeg catch bloka. Nakon
izvršenja naredbi u unutrašnjem catch bloku, izvršava se unutrašnji finally blok. Program
nastavlja sa izvršavanjem nakon finally bloka (u spoljašnjem try bloku). Spoljni try blok izbacuje
izuzetak DivideByZeroException koji je uhvaćen od strane spoljašnjeg catch bloka. Nakon
izvršenja spoljašnjeg catch bloka, izvršava se spoljašnji finally blok.

using System;
class NestedTry
{
static void Main()
{
int x = 10, y = 0, z = 0;
int[] p = new int[3];
try
{
try
{
p[3] = 23;
Console.WriteLine("Element niza je {0}", p[0]);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Izuzetak se pojavio, indeks van opsega - " +
ex.ToString());
}
finally
{
Console.WriteLine("Ovo je unutrasnji finally blok");
}
z = x / y;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Pojavio se izuzetak - deljenje nulom ", ex);
}
finally
{
Console.WriteLine("Ovo je spoljasnji finally blok");
}
Console.ReadKey();
}
}

Izlaz:
Izuzetak se pojavio, indeks van opsega - System.IndexOutOfRangeException: Index was
outside the bounds of the array.
at NestedTry.Main()
Ovo je unutrasnji finally blok
Pojavio se izuzetak - deljenje nulom
Ovo je spoljasnji finally blok

Poruka Izuzetak se pojavio, indeks van opsega se prikazuje kada unutrašnji catch blok obrađuje
IndexOutOfRangeException, koji je izbačen od strane unutrašnjeg try blokai. Tada se prikazuje
poruka Ovo je unutrasnji finally blok nakon izvršenja unutrašnjeg finally bloka. Poruka Pojavio
se izuzetak - deljenje nulom prikazana je od strane spoljašnjeg catch bloka kada je izuzetak
DivideByZeroException izbacio spoljašnji try blok. Poruka Ovo je spoljasnji finally blok se
prikazuje nakon izvršenja spoljnog finally bloka.

Kreiranje sopstvenog izuzetka


Iako .NET Framevork sadrži sve vrste izuzetaka koji su dovoljni da obrađuju većinu grešaka, on
takođe pruža mogućnost za kreiranje sopstvenih prilagođenih izuzetaka. Prilagođeni izuzeci
pomažu da se pojednostavi kod i poboljša rukovanje greškama. Prilagođeni izuzeci moraju da
naslede ili Sistem.Exception klasu ili jednu od njenih standardno izvedenih klasa, kao što su
SistemException ili ApplicationException. SistemException je namenjen za one izuzetke koje
definiše CLR, dok ApplicationException izbacuje korisnički program. Preporučuje se da nove
klase izuzetaka koje definišete budu izvedene iz klase Exception. U svom najjednostavnijem
obliku, prilagođena klasa izuzetaka samo treba da ima ime, kao što je ovde i prikazano:

public class CustomException: Exception


{
}

Možete videti da je osnovni prilagođeni izuzetak pod nazivom CustomException kreiran


izvođenjem iz klase Exception. Nakon dodavanja konstruktora, prilagođeni izuzetak se pojavljuje
kao što je prikazano u narednom primeru:

public class CustomException: Exception


{
public CustomException()
{
}
public CustomException(string str): base(str)
{
}
}

Sa prethodnim formatom koda korisnički prilagođeni izuzetak se ponaša kao i drugi standardni
izuzeci. Može se još “baciti” CustomException instanca i proslediti string koji opisuje uzrok
greške. Program CustomException.cs prikazan u sledećem listingu pokazuje kreiranje
prilagođenog izuzetka pod nazivom CustomException. Program definiše CustomException,
ručno izbacuje izuzetak i hvata ga sa potrebnim catch blokom.

using System;
class CustomException : Exception
{
public CustomException()
{
Console.WriteLine("Moj vlastiti izuzetak");
}
public CustomException(string str) : base(str)
{
}
}
class CreateException
{
public static void Main()
{
try
{
throw new CustomException("Zdravo!");
}
catch (Exception ex)
{
Console.WriteLine("CustomException je uhvacen − " + ex.ToString());
}
Console.ReadKey();
}
}

Izlaz:
CustomException je uhvacen − CustomException: Zdravo!
at CreateException.Main()

Može se videti da je CustomException ručno izbačen pomoću funkcije throw u try bloku.
Poruka koja je prosleđena prilikom izbacivanja izuzetka je Zdravo!, ali to može biti bilo koji tekst
koji objašnjava uzrok greške. Poziva se parametarizovani konstruktor CustomException, koji
zauzvrat poziva konstruktor osnovne klase (Exception klasa). Izbačeni CustomException se
obrađuje u catch bloku definisanim u glavnom metodu. Catch blok prikazuje poruku koja je
izazvala izuzetak.

Korišćenje potvrđenog (označenog)/nepotvrđenog operatora


Potvrđeni i nepotvrđeni (checked/unchecked) operatori određuju da li će u slučaju
prekoračenja program izbaciti izuzetak ili ne. Mora se odlučiti da li da se greška prekoračenja
otkrije ili da se ignoriše.
- Potvrđeni (checked) operator nameće proveru prekoračenja tako što izbacuje izuzetak ako
dođe do prekoračenja. Ako je blok koda označen kao potvrđen, CLR proverava da li je došlo do
prekoračenja i izbacuje OverflowException ako je do prekoračenja zaista i došlo.
- Neoznačeni operator ne generiše izuzetak ako dođe do prekoračenja. On jednostavno
potiskuje proveru prekoračenja u kodu, čime se gube podaci u kodu koji je označen kao
nepotvrđen.

Beleška
Neoznačeno (unchecked) stanje je podrazumevano stanje u C#.
U sledećem listing program CheckedExample.cs pokazuje primenu potvrđenih i nepotvrđenih
operatora u otkrivanju uslova prekoračenja.

using System;
class CheckedExample
{
static void Main()
{
int a;
byte b;
a = Int32.MaxValue;
b = byte.MaxValue;
Console.WriteLine("Maksimalna vrednost Int32 je {0}", a);
Console.WriteLine("Maksimalna vrednost bajta je {0}", b);
try
{
checked
{
a++;
b++;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
Console.WriteLine("Izuzetak se izbacuje. Int32 nece biti uvecan.Vrednost ostaje {0}", a);
Console.WriteLine("Izuzetak se izbacuje. Bajt nece biti uvecan.Vrednost ostaje {0}", b);
try
{
unchecked
{
a++;
b++;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
}
Console.WriteLine("Int32 posle uvecanja za 1 postaje {0}", a);
Console.WriteLine("Bajt tip posle uvecanja za 1 postaje {0}", b);
Console.ReadKey();
}
}

Izlaz:
Maksimalna vrednost Int32 je 2147483647
Maksimalna vrednost bajta je 255
Arithmetic operation resulted in an overflow.
Izuzetak se izbacuje. Int32 nece biti uvecan.Vrednost ostaje 2147483647
Izuzetak se izbacuje. Bajt nece biti uvecan.Vrednost ostaje 255
Int32 posle uvecanja za 1 postaje −2147483648
Bajt tip posle uvecanja za 1 postaje 0
Bajt može da sadrži vrednosti u opsegu od 0 do 255, a Int32 može da sadrži vrednosti u opsegu
od 0 do 2147483647. Povećanje bajta ili Int32 će dovesti do prekoračenja. Nakon zapisivanja
koda kao označenog, CLR nameće proveru prekoračenja. Izbacuje OverflowException nakon
povećanja vrednosti bajta ili Int32, prikazujući poruku Aritmetička operacija je rezultirala
prekoračenjem, tj. na engleskom Arithmetic operation resulted in an overflow.
Prednost je u tome što ne dobijate pogrešne rezultate nakon što prikažete kod kao označen
(probveren). Nakon što prikažete kod kao neoznačen, suzbijate proveru prekoračenja, čime
gubite podatke. Tip bajta ne može da zadrži vrednost od 256, a bitovi na kojima se javlja
prekoračenje se odbacuju i celoj promenljivoj b se dodeljuje vrednost 0. Isto se dešava sa Int32;
ne može da zadrži vrednost 2147483648, pa dodeljuje vrednost –2147483648 (pogrešna
vrednost) Int32 promenljivoj a.

Rezime
U ovom poglavlju naučili ste pojedinosti o različitim tipovima grešaka, ulozi izuzetaka i različitim
ključnim rečima koje su potrebne za rukovanje izuzecima. Naučili ste da koristite try-catch-
finally blokove, višestruke try blokove, višestruke catch blokove, kao i CatchAll izuzetke. Takođe
ste videli kako da ručno “izbacite” bilo koji izuzetak i kako da ponovo “izbacite” izuzetak. Naučili
ste da ugnezdite try blokove, kreirate sopstvene prilagođene izuzetke i primenite potvrđene i
nepotvrđene operatore u proveravanju prekoračenja u numeričkim proračunima.

You might also like