среда, 30. децембар 2015.

Upravljanje greškama i izuzecima u C# programskom jeziku


Bez obzira koliki ste guru za programiranje i koliko je vaše kodiranje kvalitetno, vaš program mora biti u mogućnosti da rukuje sa greškama. Greške mogu da se dese u bilo kojoj etapi izvršavanja programa i većina njih nije izazvana greškom u vašoj aplikaciji. Otkrivati, pretpostavljati, uočiti ili ispravljati greške u programu je često jedan od najtežih poslova programera i zbog toga je ovaj proces podležan mnogim testiranjima. Što se pre otkrije neka greška ona će vas manje koštati u daljem procesu razvoja vašeg projekta. Srećom, Visual Studio sadrži odlične alate za rad sa greškama koje će vam itekako olakšati rad sa upravljanjem grešaka. Međutim, vi trebate da perfektno poznajte i koristite alate Microsoft Visual Studio-a. Takođe rad sa izuzecima vam ne sme biti nešto nepoznato. Njihova primena u praksi mora biti perfektna dok to nije slučaj za vreme edukacije.


( Upravljenje greškama je najbitnija stvar u programiranju )

Postoje 3 osnovna tipa grešaka. Sintaksne greške koje kompajler ne može da razume, greške u vreme izvršavanja koje se javljaju kad se pokuša izvršiti proces koji se ne može sprovesti i logičke greške koje se javljaju kad kompajler ne prijavljuje grešku ali vi ne dobijate očekivane rezultate. Sintaksne greške se najlakše ispravljaju jer kad u kodu otkucate nepravilno ključne reči ili kad nešto zaboravite da otkucate, Microsoft Visual Studio automatski otkriva i podvlači greške crvenom cik cak linijom i kompajler neće kompilirati program. Međutim, IDE će takođe otkrivene greške dodati Task List prozoru obezbeđujući dovoljno informacija za ispravljanje greške. Dodatnu pomoć možete dobiti pritiskom F1 na tastaturi. Greške u vreme izvršavanja su vam greške kao kad na primer pokušate da sprovedete nedozvoljenu radnju deljenjem nulom. Kod ovakvih grešaka ispaljuje se izuzetak i vi dobijate poruku koja opisuje grešku. Logičke greške je najteže otkriti jer često ne postoji nikakav nagoveštaj da greška postoji dok ne vidite da su očekivani rezultati vašeg programa pogrešni. Srećom postoje alati koje vam omogućavaju Break režim.

Šta je Break režim? 




Režim Break vam omogućava da zaustavite izvršavanje vašeg programa i da onda izvršavate jednu po jednu liniju koda. Dovoljan je jedan klik levim tasterom miša na levoj ivici Code Editor-a pored linije koda gde želite da zaustavite program i napravili ste Break Point u obliku crvene tačke. Pogledajte sliku:


( Break Point-s u Microsoft Visual Studio-u )

Sad kad pokrenete program u Debug modu, program će stopirati izvršavanje programa na liniji koda gde ste postavili Break Point – tačku prekida. U toolbar meniju vam se omogućava da koristite dugmad Show Next Statement, Step Into, Step Over i Step Out sa kojim nastavljate da izvršavate liniju po liniju koda dok vam se ne pojavi izuzetak u delu koda gde ste imali grešku. U Break režimu, dovoljno je da postavite pokazivač miša na bilo koju promenjivu i možete videti vrednost promenjive tokom izvršavanja linije koda po liniju koda. Takođe možete uključiti dodatne prozore poput QuickWatch… ili Show Diagnostic Tools. Možete imati više tački prekida dok njihovo uklanjanje je takođe jednostavno. Samo kliknete levim tasterom miša na njih i one će nestati. Bez ovih mogućnosti u Microsoft Visual Studio-u vi bi ste kao i neki početnici pisali poruke da vas obaveštavaju kolika je vrednost promenjive posle svake operacije na njoj. Ovako kad jednom savladate i steknete navike da koristite alate, mnogo lakše, pametnije i češće će te ih koristiti.

Šta su izuzeci?

Izuzeci su instance specijalizovani klasa koje su sve izvedene iz bazne klase System.Exception. Izuzeci uključuju svojstva koja olakšavaju dijagnozu i rukovanje greškom poput svojstva Message koji može da sadrži čitljiv opis greške kao i bilo koje druge važne informacije o greški ili svojstvu StackTrace, koji sadrži Trace – zapis steka da bi se omogućilo praćenje i precizno pronalaženje mesta gde se pojavila greška u kodu. Sa strukturiranim razrešavanjem izuzetaka možete obezbediti sredstva za oporavak vaše aplikacije u slučaju neočekivanih grešaka ili da barem obezbedite šansu da se podaci sačuvaju pre zatvaranja aplikacije. Da bi se rukovalo izuzecima u C# programskom jeziku se koristi rukovalac greškama, preciznije konstrukcijom try-catch-finally iskaza. Rad sa izuzecima u C++ programskom jeziku je na sličan način kao u C# programskom jeziku, samo što u C++ programskom jeziku programeri obazrivo koriste izuzetke zbog pratećeg pada performansi. U C# programskom jeziku to nije takav slučaj. Kako se radi sa izuzecima u C++ programskom jeziku možete pogledati ovde. U Visual Basic-u su greške svedene na On Error GoTo, tako da rad sa izuzecima u C# programskom jeziku jeste izuzetna prednost spram drugih programskih jezika.

Šta je rukovalac greškama?

Rukovalac greškama je try-catch-finally konstrukcija specijalno namenjena za hvatanje, sprečavanje i ispravljanje grešaka. Sintaksa za navedenu konstrukciju je sledeća:

try
{
          kod koji se izvršava do pojave greške

}
catch (exception)
{
          kod koji se izvršava ukoliko dođe do greške

}
finally
{
         kod koji se izvršava bez obzira da li je bilo greške ili nije

}


Konstrukcija try-catch-finally se često piše bez finnaly iskaza i može da sadži više catch iskaza. Ova konstrukcija može i da ne sadrži ni jedan catch iskaz i pored toga što ja ne vidim nikakav smisao tome. Pogledajte sledeći program koji pokazuje kako se try-catch-finnally konstrukcja koristi.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TryCatchFinally
{
    class Program
    {
      
        static void Main(string[] args)
        {
            bool correct = false;
            string number = String.Empty;

            do
            {
                Write(Environment.NewLine + "Enter number: ");
                number = ReadLine();

                try
                {
                    Int32.Parse(number);
                    correct = true;

                }
                catch (FormatException fe)
                {
                    correct = false;
                    Error.WriteLine($"FormatException caught: {fe.Message}" + Environment.NewLine);

                }
                catch(OverflowException oe)
                {
                    correct = false;
                    Error.WriteLine($"OverflowException caught: {oe.Message}" + Environment.NewLine);

                }
                catch(Exception ex)
                {
                    correct = false;
                    Error.WriteLine($"Exception caught: {ex.Message}" + Environment.NewLine);

                }
                finally
                {
                    WriteLine("--------------------");

                }
            } while (!correct);

            WriteLine($"Your number {number} is correct!");
            ReadKey();
        }
    }
}

Ukoliko korisnik unese karaktere koje nisu integer ili ne unese ništa; već samo pritisne taster Enter na tastaturi; aktiviraće se prvi izuzetak u try-catch-finally konstrukciji i preneti kratki opis greške korisniku. Međutim, ukoliko želite detaljnije informacije o grešci koje u praksi nisu namenjene krajnjem korisniku već samo programeru; jednostavno nemojte koristiti svojstvo Message vašeg izuzetka. Programeri inače kod velikih projekata koriste i svojstvo izuzetka StackTrace koji se uglavnom koristi kada želite da vodite evidenciju grešaka u posebnom fajlu koji sami kreirate. Upotreba StackTrace svojstva nije ista u Release i u Debug modu. Prikaz informacija je drugačiji. Vi na velikim projektima možete čak kreirati i namensku klasu koja izdvaja korisne informacije iz izuzetaka što uglavnom primenjuju testeri. U navedenom primeru, drugi izuzetak se pojavljuje kad korisnik unese veći ili manji broj od maksimalne ili minimalne vrednosti integer-a. Sa ova dva izuzetka program je faktički osiguran od grešaka. Zato se treći izuzetak, koji je namenjen za bilo koju grešku skoro nikad neće izvršiti. Međutim, praksa je da se izuzetak Exception uvek koristi u slučaju da nešto krene nepredviđeno. Često programeri u manjim programima koriste samo Exception izuzetak za hvatanje izuzetka. Bitno je da razumete da lista izuzetaka ima svoju vlastitu hijerarhiju i da samo po njenom redosledu možete koristiti više catch blokova. U prevodu Exception izuzetak ne možete staviti pre izuzetka FormatException u try-catch-finally konstrukciji. Da bi ste znali kojim redom i koje izuzetke možete koristiti, najbolje posetite MSDN – Microsoft Developer Network ovde. Takođe možete pogledati i sledeću sliku.


( Redosled Exception izuzetaka )
Kad pokrenete navedeni program, rezultati će vam biti slični u zavisnosti od vašeg unosa podataka.

Enter number: string

FormatException caught: Input string was not in a correct format.
--------------------

Enter number:

FormatException caught: Input string was not in a correct format.
--------------------

Enter number: 8888888888

OverflowException caught: Value was either too large or too small for an Int32.
--------------------

Enter number: 8
--------------------

Your number 8 is correct!


Kako izgleda prethodni program možete pogledati i na video-u:


 

( C# 6.0 Tutorial - Fundamentals - 28. Try Catch Finally Statement )

Šta je Throw Exception?

Throw Exception, misli se na Throwing Exception – izbacivanje izuzetka se koristi kada hoćete da izbacite izuzetak, prijavite grešku. Izbacivanje greške pravite iskazom throw. Ovaj iskaz možete koristiti u try-catch-finally konstrukciji; možete i na primer u iskazu switch što će te videti u sledećem primeru. Izbacivanje izuzetaka se koristi kad ne možete u potpunosti da razrešite grešku u vreme izvršavanja ali želite delimično da je obradite ili kad dođe do neprihvatljivog uslova u komponenti koji se ne može razrešiti lokalno. Izbacivanje izuzetaka se ne smatra dobrom praksom u programima ali je odlično kad je u pitanju testiranje.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ThrowingException
{
    class Program
    {
        public static string Day(byte number)
        {
            switch (number)
            {
                case 1:
                    return "Monday";

                case 2:
                    return "Tuesday";

                case 3:
                    return "Wednesday";

                case 4:
                    return "Thursday";

                case 5:
                    return "Friday";

                case 6:
                    return "Saturday";

                case 7:
                    return "Sunday";

                default:
                    throw new ArgumentOutOfRangeException("There are only 7 days in a week!");

            }

        }

        static void Main(string[] args)
        {           
            bool correct = false;
            string number = String.Empty;

            do
            {
                Write("Enter number of day: ");
                number = ReadLine();

                try
                {
                    WriteLine($"{Day(Byte.Parse(number))}");
                    correct = true;

                }
                catch (Exception ex)
                {
                    correct = false;
                    Error.WriteLine($"Error: {ex.Message}" + Environment.NewLine);

                }
                  
              } while (!correct);

              ReadKey();
           
        }       
    }
}
     
Ukoliko korisnik ne unese broj tipa byte dobiće grešku. Međutim ako unese 0 ili veći broj od 7, našim izbacivanjem izuzetka će mu se takođe pojaviti greška i opis koji smo sami kreirali. Kad pokrenete navedeni program rezultati će biti slični u zavisnosti od unosa podataka.

Enter number of day:

Error: Input string was not in a correct format.



Enter number of day: 0

Error: Specified argument was out of the range of valid values.

Parameter name: There are only 7 days in a week!



Enter number of day: 2

Tuesday

Kako izgleda prethodni program možete pogledati i na video-u:


( C# 6.0 Tutorial - Fundamentals - 29. Throwing Exception )

Šta su ključne reči checked i unchecked?

Ključne reči checked i unchecked su preciznije kontrole izuzetaka prekoračenja, koju često zovu operatorima i koje sam spominjao u postu Operatori u C# programskom jeziku. Na primer ova linija koda će biti momentalno označena kao da je u pitanju sintaksna greška i pre kompajliranja.

int number = Int32.MaxValue + 10;

Međutim ako maksimalnoj integer vrednosti dodamo 10 na implicitni način:

int ten = 10;
int number = Int32.MaxValue + ten;

Onda kompajler neće prijaviti grešku ali će vrednost vaše promenjive biti -2147483639 jer će kompajler od maksimalne vrednosti oduzeti vrednost 10 koji prekoračuje maksimalno dozvoljenu vrednost. Međutim, takva vrednost vam definitivno ne treba u programu i zato koristite ključnu naredbu checked da omogući kompajleru da prijavi grešku i prekine izvršavanje programa.

checked
{
int ten = 10;
       int number = Int32.MaxValue + ten;
       WriteLine(number);

}

Međutim ako vama odgovara da rezultat bude -2147483639, samo da ne prijavljuje grešku kad dođe do prekoračenja, tada koristite iskaz uncheked koji će sprečiti ispaljivanje izuzetka.

unchecked
{
       int number = Int32.MaxValue + 10;
       WriteLine(number);

}   

Jedino morate voditi računa da se navedene iskaze koriste jedino kod celobrojnih vrednosti. Pogledajte sledeći program, koji koristi obadve metode.

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CheckedAndUncheckedKeywords
{
    class Program
    {
        static int maxIntValue = Int32.MaxValue;

        // Using an checked expression.
        static int CheckedMethod()
        {
            int z = 0;
            try
            {
                // The following line raises an exception because it is checked.
                z = checked(maxIntValue + 10);
            }
            catch (System.OverflowException e)
            {
                // The following line displays information about the error.
                WriteLine("CHECKED and CAUGHT:  " + e.Message.ToString());
            }
            // The value of z is still 0.
            return z;
        }

        // Using an unchecked expression.
        static int UncheckedMethod()
        {
            int z = 0;
            try
            {
                // The following calculation is unchecked and will not raise an exception.
                z = maxIntValue + 10;
            }
            catch (System.OverflowException e)
            {
                // The following line will not be executed.
                WriteLine("UNCHECKED and CAUGHT:  " + e.Message.ToString());
            }
            // Because of the undetected overflow, the sum of 2147483647 + 10 is
            // returned as -2147483639.
            return z;
        }

        static void Main()
        {
            WriteLine(Environment.NewLine + $"CHECKED output value is: {CheckedMethod()}");
            WriteLine($"UNCHECKED output value is: { UncheckedMethod()}");

            ReadKey();

        }
    }
}

Kad pokrenete navedeni program, rezultat će biti sledeći:

CHECKED and CAUGHT: Arithmetic operation resulted in an overflow.

CHECKED output value is: 0

UNCHECKED output value is: -2147483639


Kako izgleda prethodni program možete pogledati i na video-u:


( C# 6.0 Tutorial - Fundamentals - 30. Checked And Unchecked Keywords )