Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • Sfat
  • Erori tipice ale codului si sharp. Depanarea și gestionarea excepțiilor

Erori tipice ale codului si sharp. Depanarea și gestionarea excepțiilor

Bazele gestionării excepțiilor

Erorile nu sunt întotdeauna din vina persoanei care codifică aplicația. Uneori, o aplicație generează o eroare din cauza acțiunilor utilizatorului final sau eroarea este cauzată de contextul mediului în care rulează codul. În orice caz, ar trebui să vă așteptați întotdeauna la erori în aplicațiile și codul dvs. conform acestor așteptări.

.NET Framework oferă o gestionare avansată a erorilor. Mecanismul de gestionare a erorilor C # vă permite să codificați gestionarea personalizată pentru fiecare tip de condiție de eroare și, de asemenea, să decuplați codul care generează erori de codul care le gestionează.

Indiferent de ce cauzează problema, aplicația va începe în cele din urmă să se comporte diferit decât se aștepta. Înainte de a trece la gestionarea structurată a excepțiilor, să aruncăm o privire la cei trei termeni cei mai des folosiți pentru a descrie anomaliile:

Erori software

Acesta este de obicei numele erorilor pe care le face programatorul. De exemplu, să presupunem că o aplicație este construită folosind C++ negestionat. Dacă memoria alocată dinamic nu este eliberată, care este plină de o scurgere de memorie, apare o eroare de programare.

Erori utilizator

Spre deosebire de erorile software, erorile personalizate sunt de obicei cauzate de oricine rulează aplicația, nu de oricine o creează. De exemplu, un utilizator final care introduce un șir formatat incorect într-un câmp de text poate genera o eroare de acest fel dacă codul nu a fost conceput pentru a gestiona introducerea incorectă.

Excepții

Excepțiile sau excepțiile sunt denumite în mod obișnuit anomalii care pot apărea în timpul execuției și care sunt dificil, dacă nu imposibil, de anticipat la programarea unei aplicații. Aceste posibile excepții includ încercarea de a se conecta la o bază de date care nu mai există, încercarea de a deschide un fișier corupt sau încercarea de a comunica cu o mașină care este în prezent offline. În fiecare dintre aceste cazuri, este puține lucruri pe care programatorul (și utilizatorul final) poate face cu privire la aceste circumstanțe „excepționale”.

Din descrierile de mai sus, ar trebui să fie clar că gestionarea structurată a excepțiilorîn .NET este o tehnică concepută pentru a face față excepțiilor care pot fi aruncate în timpul execuției. Chiar și în cazul erorilor de programare și utilizator care au scăpat de ochii programatorului, totuși, CLR va genera adesea automat o excepție adecvată care descrie problema curentă. Bibliotecile de clasă de bază .NET definesc multe excepții diferite, cum ar fi FormatException, IndexOutOfRangeException, FileNotFoundException, ArgumentOutOfRangeException etc.

În terminologia .NET, „excepție” se referă la erori de programare, erori ale utilizatorului și erori de rulare. Înainte de a pătrunde în detalii, să vedem ce rol joacă gestionarea excepțiilor structurate și cum diferă de tehnicile tradiționale de tratare a erorilor.

Rolul gestionării excepțiilor în .NET

Înainte de apariția .NET, gestionarea erorilor în sistemul de operare Windows era un amestec complex de tehnologii. Mulți programatori și-au încorporat propria logică de gestionare a erorilor în contextul aplicării de interes. De exemplu, echipa de dezvoltare ar putea defini un set de constante numerice pentru a reprezenta eșecurile cunoscute și apoi să folosească acele constante ca valori returnate pentru metode.

Pe lângă trucurile inventate de dezvoltatorii înșiși, API-ul Windows definește sute de coduri de eroare folosind #define și HRESULT, precum și multe variații ale valorilor booleene simple (bool, BOOL, VARIANT BOOL etc.). Mai mult decât atât, mulți dezvoltatori C ++ de aplicații COM (precum și VB 6) folosesc în mod explicit sau implicit un mic set de interfețe COM standard (cum ar fi ISupportErrorlnfo. IErrorlnfo sau ICreateErrorlnfo) pentru a returna informații de eroare semnificative clientului COM.

Problema evidentă cu toate aceste tehnici mai vechi este lipsa de simetrie. Fiecare dintre ele se încadrează mai mult sau mai puțin în cadrul unei tehnologii, al unei limbi și, poate, chiar al unui proiect. B.NET menține o tehnică standard pentru generarea și detectarea erorilor în mediul de rulare numit gestionarea structurată a excepțiilor (SEH) .

Frumusețea acestei tehnici este că le permite dezvoltatorilor să adopte o abordare uniformă a gestionării erorilor, care este comună tuturor limbilor care vizează platforma .NET. Acest lucru permite unui programator C # să gestioneze erorile în același mod sintactic ca un programator VB și un programator C ++ folosind C ++ / CLI.

Un avantaj suplimentar este că sintaxa necesară pentru a arunca și a captura excepții în afara ansamblurilor și mașinilor arată, de asemenea, la fel. De exemplu, dacă scrieți un serviciu Windows Communication Foundation (WCF) în C #, puteți arunca o excepție SOAP pentru un apelant de la distanță folosind aceleași cuvinte cheie care sunt folosite pentru a arunca o excepție în cadrul metodelor din aceeași aplicație.

Ultima actualizare: 23.10.2018

Uneori, la executarea unui program, apar erori care sunt greu de prevăzut sau de prevăzut, iar uneori sunt complet imposibile. De exemplu, când transferați un fișier printr-o rețea, conexiunea la rețea se poate opri în mod neașteptat. astfel de situații se numesc excepții. Limbajul C # oferă dezvoltatorilor capacitatea de a gestiona aceste situații. try ... catch ... finally construct este conceput pentru aceasta în C #.

Încearcă () prinde () în sfârșit ()

Când utilizați un bloc try ... catch..finally, toate instrucțiunile din blocul try sunt executate mai întâi. Dacă nu au fost aruncate excepții în acest bloc, atunci după execuția lui, blocul final începe să se execute. Și apoi construcția try..catch..finally își finalizează munca.

Dacă o excepție este aruncată în blocul try, ordinul normal de execuție se oprește și CLR începe să caute un bloc catch care poate gestiona excepția. Dacă se găsește blocul catch dorit, atunci acesta este executat, iar după ce se termină, blocul final este executat.

Dacă blocul de captură necesar nu este găsit, atunci când apare o excepție, programul se va prăbuși.

Luați în considerare următorul exemplu:

Program de clasă (static void Main (șir argumente) (int x = 5; int y = x / 0; Console.WriteLine ($ "Rezultat: (y)")); Console.WriteLine ("Sfârșitul programului"); ​​​Consolă.Citește ();))

În acest caz, numărul se împarte la 0, ceea ce va duce la aruncarea unei excepții. Și când pornim aplicația în modul de depanare, vom vedea o fereastră în Visual Studio care informează despre excepție:

În această fereastră, vedem că a fost aruncată o excepție, care este de tip System.DivideByZeroException, adică o încercare de împărțire la zero. Folosind elementul Vizualizare detalii, puteți vizualiza informații mai detaliate despre excepție.

Și în acest caz, singurul lucru care ne rămâne este să finalizăm execuția programului.

Pentru a evita o astfel de terminare anormală a programului, ar trebui să utilizați construcția try ... catch ... finally pentru a gestiona excepțiile. Deci, să rescriem exemplul după cum urmează:

Class Program (static void Main (șir argumente) (try (int x = 5; int y = x / 0; Console.WriteLine ($ „Rezultat: (y)”));) catch (Console.WriteLine ("O excepție a fost aruncat! ");) în cele din urmă (Console.WriteLine ("în final bloc");) Console.WriteLine ("Sfârșitul programului"); Console.Read ();))

În acest caz, vom avea din nou o excepție în blocul try, deoarece încercăm să împărțim la zero. Și ajungând la linie

Int y = x / 0;

programul se va opri din rulare. CLR găsește blocul de captură și transferă controlul către acel bloc.

După blocul catch, blocul final va fi executat.

S-a făcut o excepție! În cele din urmă blocați Sfârșitul programului

Astfel, programul încă nu va efectua împărțirea la zero și, în consecință, nu va afișa rezultatul acestei împărțiri, dar acum nu se va prăbuși, iar excepția va fi gestionată în blocul catch.

Trebuie remarcat faptul că un bloc de încercare este necesar în această construcție. Cu un bloc catch, putem omite blocul final:

Încercați (int x = 5; int y = x / 0; Console.WriteLine ($ "Rezultat: (y)");) catch (Console.WriteLine ("A apărut o excepție!");)

În schimb, cu un bloc final, putem omite blocul catch și nu gestionăm excepția:

Încercați (int x = 5; int y = x / 0; Console.WriteLine ($ "Rezultat: (y)");) în cele din urmă (Console.WriteLine ("în final bloc");)

Cu toate acestea, deși din punctul de vedere al sintaxei C #, această construcție este destul de corectă, totuși, deoarece CLR nu poate găsi blocul catch necesar, excepția nu va fi gestionată, iar programul se va bloca.

Manipularea excepțiilor și construcții condiționate

Un număr de excepții pot fi prevăzute de dezvoltator. De exemplu, să presupunem că un program oferă intrarea unui număr și ieșirea pătratului său:

Static void Main (șir argumente) (Console.WriteLine ("Introduceți un număr"); int x = Int32.Parse (Console.ReadLine ()); x * = x; Console.WriteLine ("Număr pătrat:" + x) ; Consola.Citiți ();)

Dacă utilizatorul introduce nu un număr, ci un șir, alte caractere, atunci programul va cădea într-o eroare. Pe de o parte, aceasta este exact situația în care puteți folosi un bloc try..catch pentru a gestiona o posibilă eroare. Cu toate acestea, ar fi mult mai optim să verificați validitatea conversiei:

Static void Main (args șir) (Console.WriteLine ("Introduceți un număr"); int x; șir de intrare = Console.ReadLine (); if (Int32.TryParse (input, out x)) (x * = x; Console . WriteLine ("Pătrat al numărului:" + x);) else (Console.WriteLine ("Intrare nevalidă");) Console.Read ();)

Metoda Int32.TryParse () returnează true dacă transformarea poate fi efectuată și false dacă nu poate. Dacă conversia este validă, variabila x va conține numărul introdus. Deci, fără a utiliza try ... catch, puteți gestiona o posibilă excepție.

Din punct de vedere al performanței, folosirea blocurilor try..catch este mai costisitoare decât utilizarea condiționalelor. Prin urmare, ori de câte ori este posibil, în loc de try..catch, este mai bine să folosiți constructe condiționate pentru a verifica excepțiile.

constante de compilare condițională... Permite definirea constantelor utilizate ulterior în metoda de imprimare condiționată WriteIf a claselor Debug și Trace. Puterea acestui mecanism este că constantele pot fi modificate în fișierul de configurare a proiectului fără a modifica codul proiectului sau a necesita recompilare.

Depanare și Visual Studio .Net Toolbox

Mediul instrumental al studioului oferă programatorului cea mai largă gamă de posibilități pentru urmărirea progresului calculelor și urmărirea stărilor în care se află procesul de calcul. Deoarece toate mediile moderne de instrumente sunt organizate într-un mod similar și sunt bine cunoscute programatorilor care lucrează, nu mă voi opri asupra descrierii capacităților mediului.

Tratarea excepțiilor

Oricât de fiabil este scris codul, oricât de minuțioasă este depanarea, în versiunea depusă pentru producție și întreținere, vor exista încălcări ale specificațiilor la lansare. Motivele pentru aceasta sunt legile menționate mai sus tehnicieni de programe... Ultima eroare rămâne în sistem, există utilizatori care nu cunosc specificațiile, iar dacă specificația poate fi încălcată, atunci acest eveniment se va întâmpla cândva. Astfel de situatii exceptionale continuarea programului fie devine imposibilă (o încercare de a efectua o operație neautorizată de împărțire la zero, încercări de a scrie într-o zonă de memorie protejată, o încercare de a deschide un fișier inexistent, o încercare de a obține o înregistrare inexistentă a bazei de date), sau într-o situație care a apărut, aplicarea algoritmului va duce la rezultate eronate.

Ce să faci dacă situatie exceptionala? Desigur, există întotdeauna o modalitate standard - de a raporta eroarea care a apărut și de a întrerupe execuția programului. Este clar că acest lucru este acceptabil numai pentru aplicații inofensive; chiar și pentru jocurile pe calculator, această metodă nu este potrivită, cu atât mai puțin aplicațiile critice!

În limbaje de programare pentru procesare situatii exceptionale au fost propuse o varietate de abordări.

C / C ++ Gestionarea excepțiilor

Stilul de programare C este caracterizat prin descrierea metodelor de clasă ca funcții booleene care returnează true dacă metoda se termină normal și false dacă apare. situatie exceptionala... Apelul de metodă a fost introdus în instrucțiunea If, care tratează o eroare în cazul eșecului finalizării metodei:

bool MyMethod (...) (...) if! MyMethod () (// gestionarea erorilor) (// execuție normală)

Dezavantajele acestei scheme sunt clare. În primul rând, există puține informații despre cauza erorii, astfel încât informații suplimentare trebuie transmise fie prin câmpurile de clasă, fie prin argumentele metodei. În al doilea rând, blocul de procesare este încorporat în fiecare apel, ceea ce duce la umflarea codului.

Prin urmare, în C / C ++ se utilizează schema de blocuri try / catch, a cărei esență este următoarea. Secțiunea din program în care poate fi situatie exceptionala, se executa sub forma unui try-block pazit. Dacă în timpul executării sale are loc situatie exceptionala, atunci se întrerupe execuția try-blocului cu clasificarea excepției. Unul dintre blocurile catch care se potrivește cu tipul excepției începe să gestioneze această excepție. Există două astfel de scheme utilizate în C/C++. Unul din ei - schema de reluare- corespunde așa-numitelor excepții structurale sau C. A doua schemă este fără reînnoire- se potrivește cu excepțiile C++. În prima schemă, handlerul de excepții - blocul catch - returnează controlul la un anumit punct din blocul try. În a doua schemă, controlul nu revine la blocul de încercare.

Cu unele diferențe sintactice schema de reluare folosit în limbaje VB / VBA.

Schema de gestionare a excepțiilor în C #

Limbajul C # a moștenit schema de excepție C ++, făcându-i propriile ajustări. Să luăm în considerare schema mai detaliat și să începem cu sintaxa construcției încercați-prindeți-în sfârșit:

încercați (...) prinde (T1 e1) (...) ... prinde (Tk ek) (...) în sfârșit (...)

Oriunde un bloc este permis sintactic într-un text modul, blocul poate fi protejat prin adăugarea cuvântului cheie try. Blocul try poate fi urmat de blocuri catch numite blocuri de gestionare a excepțiilor, pot fi mai multe dintre ele, pot fi absente. Completează această secvență în sfârșit blocați- bloc de finalizare, care poate fi și absent. Toată această construcție poate fi imbricată - blocul de încercare poate include construcția încercați-prindeți-în sfârșit.

Aruncând excepții. Crearea obiectelor de excepție

Corpul unui bloc try poate conține situatie exceptionala duce la aruncând excepții... Oficial aruncând o excepție apare atunci când este executată o instrucțiune throw. Această declarație este cel mai adesea executată în măruntaiele sistemului de operare atunci când sistemul de comandă sau funcția API nu își poate face treaba. Dar această declarație poate face parte din textul programului blocului de încercare și poate fi executată atunci când, în urma analizei, devine clar că operarea normală ulterioară este imposibilă.

Sintactic, declarația throw este:

arunca [expresie]

Clauza throw specifică un obiect al unei clase care moștenește din clasa Exception. Aceasta este de obicei o nouă expresie care creează un nou obiect. Dacă este absentă, atunci excepția curentă este aruncată din nou. Dacă o excepție este aruncată de sistemul de operare, atunci acesta clasifică excepția în sine, creează un obiect din clasa corespunzătoare și completează automat câmpurile acesteia.

În modelul pe care îl luăm în considerare, excepțiile sunt obiecte, a căror clasă este un descendent al clasei Exception. Această clasă și numeroșii săi descendenți fac parte din FCL, deși sunt împrăștiați în diferite spații de nume. Fiecare clasă definește un anumit tip de excepție în conformitate cu clasificarea adoptată în .Net Framework. Iată doar câteva clase de excepție din spațiul de nume System: ArgumentException, ArgumentOutOfRangeException, ArithmeticException, BadImageFormatException, DivideByZeroException, OverflowException. Spațiul de nume System.IO conține clase de excepție legate de problemele I/O: DirectoryNotFoundException, FileNotFoundException și multe altele. Numele tuturor clase de excepție se încheie cu cuvântul Excepție. Aveți voie să vă creați propria dvs clase de excepție prin moştenirea lor din clasa Exception.

Când se execută instrucțiunea throw, este creat un obiect te, a cărui clasă TE caracterizează excepția curentă, iar câmpurile conțin informații despre situatie exceptionala... Executarea instrucțiunii throw face ca procesul normal de calcul să se încheie acolo. Dacă acest lucru se întâmplă într-un bloc de încercare păzit, atunci etapa începe „prinzând” o excepție unul dintre manipulatorii de excepții.

Prinderea unei excepții

Blocul catch - handlerul de excepții are următoarea sintaxă:

prinde (T e) (...)

Clasa T specificată în antetul catch-block trebuie să aparțină clase de excepție... Un bloc catch cu un argument formal e al clasei T este potențial capabil să prindă excepția curentă te a clasei TE dacă și numai dacă te este compatibil cu e. Cu alte cuvinte, captarea potențială înseamnă că atribuirea e = te este validă, ceea ce este posibil atunci când TE este un descendent al lui T. Un handler a cărui clasă T este clasa Exception este manipulator universal, este posibil să prindă orice excepție, deoarece toți sunt descendenți ai acesteia.

Pot exista mulți potențiali invadatori; doar unul prinde o excepție - cel care este primul în lista de verificare. Care este comanda de cec? Este destul de natural. În primul rând, manipulatorii sunt verificați în ordinea în care urmează blocul de încercare, iar primul invadator potențial devine activ, prind excepția și o gestionează. Acest lucru face clar că ordinea blocurilor de captură este extrem de importantă. Primele sunt cele mai multe manipulatori specializati, pe măsură ce versatilitatea crește. Deci, mai întâi ar trebui să existe un handler de excepție DivideByZeroException, urmat de Main. Dacă nu există un potențial exception catcher în el, atunci handlerul standard va fi declanșat, întrerupând execuția programului și emitând un mesaj corespunzător.

O zi buna! Astăzi vom vorbi despre cum să gestionăm erorile din programele scrise în C#.

Vreau să observ imediat că erorile din programe pot fi împărțite condiționat în două grupuri: erori la momentul construirii programului (în etapa de compilare) și erorile la timpul de execuție a programului. Acest tutorial va vorbi despre erorile de rulare!

Să ne amintim un exemplu din lecția anterioară în care utilizatorul trebuia să introducă două numere întregi de la tastatură. Apoi am tot cerut să introduc exact numere și exact numere întregi. De ce? Până când, pentru că dacă introduci în acel program nu un număr, ci doar text, sau nu un întreg, ci un număr fracționar, atunci va apărea o eroare de rulare! Tocmai din acest motiv vă vorbesc astăzi despre mecanismele și principiile de tratare a erorilor în programe.

Așadar, dacă bănuim că programul nostru poate avea erori de rulare, de exemplu, din cauza faptului că utilizatorul a introdus date incorecte, trebuie să avem grijă de gestionarea acestor erori. Abordarea gestionării unor astfel de situații constă în următoarele reguli: se alocă un bloc de cod, în timpul execuției căruia pot apărea erori; este alocat un bloc de cod care este responsabil pentru gestionarea erorilor care au apărut.

Un bloc de cod în care pot apărea erori este indicat printr-un cuvânt cheie încerca urmate de bretele (care includ operațiuni potențial periculoase). Un astfel de bloc ar trebui să fie imediat urmat de un bloc de procesare a erorilor, este notat cu cuvântul cheie captură, iar în paranteze după acest cuvânt, denotă tipul de erori pe care blocul de date al codului le prelucrează, după paranteza de închidere, urmată de acolade, în interiorul cărora este implementată procesarea. Schematic, arată astfel:

Try (// Bloc de cod potențial periculos) catch (error_type) (// Gestionarea erorilor) // Alte instrucțiuni de program

Și funcționează așa dacă în bloc încerca, nu a apărut nicio eroare, apoi blocați captură nu este executat, iar controlul este transmis, adică blocul următor este executat captură operator. Dar dacă în interiorul blocului încerca a apărut o eroare, atunci execuția acestui bloc este întreruptă și controlul este transferat blocului captură, și numai atunci sunt executați operatorii care urmează acestui bloc.

În practică, după un bloc încerca, pot exista mai multe blocuri captură, pentru tratarea separată a diferitelor tipuri de erori.

Să modificăm codul programului din lecția anterioară, să adăugăm controlul și gestionarea erorilor care pot apărea atunci când utilizatorul introduce date incorecte.

// Bloc de încercare potențial periculos (// Solicitați utilizatorului să introducă primul număr Console.Write ("Introduceți primul număr și apăsați Enter:"); // Obțineți primul șir de linie firstString = Console.ReadLine (); // Convertiți prima linie în număr int firstArg = Convert.ToInt32 (firstString); // Solicitați utilizatorului să introducă al doilea număr Console.Write ("Introduceți primul număr și apăsați Enter:"); // Obțineți al doilea șir de caractere secondString = Console.ReadLine (); // Conversia celui de-al doilea șir într-un număr int secondArg = Convert.ToInt32 (secondString); // Adăugarea a două variabile int result = firstArg + secondArg; // Ieșirea rezultatului Console.WriteLine ("Rezultatul de adăugare a numerelor introduse:" + result.ToString ()); ) // Bloc de gestionare a erorilor, SystemException este cel mai comun tip de captură de eroare (SystemException) (Console.WriteLine ("A apărut o eroare în timpul execuției programului, probabil date incorecte) a fost introdus!");)

Acum, dacă utilizatorul introduce date incorecte în loc de un număr întreg, atunci eroarea care a apărut va fi procesată. Dacă introduceți acest cod în „ Principalul ", construiți și rulați programul, veți vedea următorul comportament:

Și astfel, în această lecție, au fost prezentate informații de bază referitoare la mecanismul de gestionare a erorilor (excepțiilor) în limbajul C#. În lecția următoare, vom vorbi despre crearea și utilizarea propriilor metode (funcții) și vom reveni la subiectul tratării erorilor de mai multe ori!

Top articole similare