Professional Documents
Culture Documents
Upravljanje Greškama I Izuzecima
Upravljanje Greškama I Izuzecima
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).
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:
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.
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.
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.
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.
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.
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:
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.
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.
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.