Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • Windows 10
  • Winforms gestionarea corectă a excepțiilor în c. Depanare și gestionarea excepțiilor

Winforms gestionarea corectă a excepțiilor în c. Depanare și gestionarea excepțiilor

constante de compilare condițională. Vă permite să definiți constante care sunt 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.

Visual Studio .Net Depanare și Workbench

Mediul instrumental al studioului oferă programatorului cel mai mult gamă largă capabilități de monitorizare a progresului calculelor și de urmărire a stărilor în care se află procesul de calcul. Pentru că toată lumea este modernă medii instrumentale sunt organizate într-un mod similar și sunt bine cunoscute programatorilor care lucrează, îmi voi permite să nu mă opresc pe descrierea capacităților mediului.

Tratarea excepțiilor

Oricare cod de încredere oricât de minuțios a fost scrisă, indiferent cât de amănunțită a fost depanarea, versiunea transferată la operare și întreținere va întâlni încălcări ale specificațiilor în timpul lansărilor. Motivul pentru aceasta este legile menționate mai sus ingineri software. Rămâne în sistem ultima greseala, 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 execuției programului sau devine imposibil (o încercare de a efectua o operație neautorizată de divizare la zero, încercări de a scrie într-o zonă de memorie protejată, o încercare de deschidere dosar inexistent, o încercare de a obține o înregistrare a bazei de date inexistentă), sau în situația care a apărut, utilizarea algoritmului va duce la rezultate eronate.

Ce să faci dacă se întâmplă acest lucru situatie exceptionala? Bineînțeles că există întotdeauna mod standard- raportați o eroare și întrerupeți execuția programului. Este clar că acest lucru este acceptabil numai pentru aplicații inofensive; chiar si pentru jocuri pe calculator Această metodă nu este potrivită, cu atât mai puțin pentru aplicații critice!

În limbaje de programare pentru procesare situatii exceptionale oferit cel mai mult abordări diferite.

Gestionarea excepțiilor în limbaje C/C++

Stilul de programare C este caracterizat prin descrierea metodelor de clasă ca Funcții booleene, returnând true dacă metoda se termină normal și false dacă apare situatie exceptionala. Apelul de metodă a fost încorporat într-o instrucțiune If care tratează o eroare dacă metoda nu se finalizează:

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, așa că fie prin câmpurile de clasă, fie prin argumentele metodei, trebuie să treceți Informații suplimentare. În al doilea rând, în fiecare apel este încorporat un bloc de procesare, ceea ce duce la umflarea codului.

Prin urmare, C/C++ utilizează schema de blocuri try/catch, a cărei esență este următoarea. Secțiunea programului în care poate apărea situatie exceptionala, este formatat ca un bloc de încercare protejat. Dacă la executarea lui există situatie exceptionala, atunci execuția blocului try cu clasificarea excepției este întreruptă. Această excepție începe să fie gestionată de unul dintre blocurile de captură corespunzătoare tipului de excepție. C/C++ folosește două astfel de scheme. Unul din ei - schema de reînnoire- corespunde așa-numitelor excepții structurale sau C. A doua schemă - nicio reînnoire- se potrivește cu excepțiile C++. În prima schemă, gestionarea excepțiilor - blocul catch - returnează controlul la un anumit punct din blocul try. În a doua schemă, controlul nu este returnat blocului try.

Cu unele diferențe sintactice schema de reînnoire folosit în limbi VB/VBA.

Schema de gestionare a excepțiilor în C#

Limbajul C# a moștenit schema de excepții a limbajului C++, făcându-i propriile ajustări. Să aruncăm o privire mai atentă asupra circuitului ș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 în textul modulului unde un bloc este permis sintactic, 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 sau pot fi absente. Completează această secvență în sfârșit blocați- bloc de finalizare, care poate lipsi și el. Întreaga structură poate fi imbricată - blocul try poate include structura încercați-prindeți-în sfârșit.

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

În corpul unui bloc de încercare poate apărea situatie exceptionala, duce la aruncând excepții. Oficial aruncând o excepție apare atunci când este executată o instrucțiune throw. Acest operator este cel mai adesea executat în profunzimile sistemului de operare, atunci când sistemul de comandă sau Funcția API nu-și poate face treaba. Dar acest operator poate face parte textul programului try block și executat atunci când, în urma analizei, devine clar că în continuare operatie normala imposibil.

Sintactic, operatorul aruncare arată astfel:

arunca [expresie]

Expresia throw specifică un obiect al unei clase care este un descendent al clasei Exception. De obicei, aceasta este o nouă expresie, care creează un nou obiect. Dacă lipsește, excepția curentă este aruncată din nou. Dacă se aruncă o excepție sistem de operare, apoi el însuși clasifică excepția, creează un obiect din clasa corespunzătoare și completează automat câmpurile acestuia.

Î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 biblioteca FCL, deși sunt împrăștiate în diferite spații de nume. Fiecare clasă specifică anumit tip excepţii conform clasificării adoptate î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, moștenindu-le 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 excepția care a apărut. situatie exceptionala. Executarea instrucțiunii throw determină oprirea procesului normal de calcul. Dacă acest lucru se întâmplă într-un bloc de încercare păzit, atunci începe pasul excepție de „prindere”. 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 blocului de captură trebuie să aparțină clase de excepție. Un bloc catch cu un argument formal e al clasei T are potențialul de a prinde excepția actuală te a clasei TE dacă și numai dacă obiectul te este compatibil cu alocarea cu obiectul e. Cu alte cuvinte, potențialul de captare înseamnă că atribuirea e = te este validă, ceea ce este posibil atunci când clasa TE este un descendent al clasei T. Un handler a cărui clasă T este o clasă Excepție este manipulator universal, este posibil să prindă orice excepție, deoarece toți sunt copii ai acesteia.

Pot exista mulți invadatori potențiali; doar unul este exclus - cel care este primul pe lista de verificare. Care este procedura de verificare? El este destul de natural. Handler-ii sunt verificați mai întâi în ordinea în care urmează blocul de încercare, iar primul potențial catcher devine activ, prinde excepția și o gestionează. Din aceasta devine clar că ordinea în lista blocurilor de captură este extrem de importantă. Majoritatea vin pe primul loc procesoare specializate, pe măsură ce universalitatea crește. Deci, mai întâi ar trebui să existe un handler de excepție DivideByZeroException, apoi Main . Dacă nu există un potențial grabber de excepții, atunci handlerul standard va funcționa, întrerupând execuția programului și emitând un mesaj corespunzător.

Prinderea de excepții

În timp ce .NET Framework include un numar mare de clase de excepție predefinite, se pune întrebarea: cum să le folosiți în cod pentru a detecta condițiile de eroare? Pentru a face față posibilelor situații de eroare în codul C#, programul este de obicei împărțit în blocuri de trei tipuri diferite:

    Blocuri încerca codul încapsulat care face parte din operațiunile normale ale unui program care ar putea întâmpina situații de eroare grave.

    Blocuri capturăîncapsulați codul care gestionează situațiile de eroare care apar în codul de blocare try. Acesta este, de asemenea, un loc convenabil pentru a înregistra erorile.

    Blocuri in cele din urma codul încapsulat care curăță orice resurse sau efectuează alte acțiuni care ar fi efectuate în mod normal la sfârșitul blocurilor try sau catch. Este important să înțelegeți că acest bloc este executat indiferent dacă se aruncă sau nu o excepție.

Încearcă și prinde

Baza gestionării excepțiilor în C# este perechea de cuvinte cheie try and catch. Aceste cuvinte cheie funcționează împreună și nu pot fi utilizate separat. Mai jos este forma generala definiții ale blocurilor try/catch pentru gestionarea excepțiilor:

try ( // Blocul de cod verificat pentru erori. ) catch (ExcepType1 exOb) ( // Gestionar excepție de tip ExcepType1. ) catch (ExcepType2 exOb) ( // Gestionar excepție de tip ExcepType2. ) ...

unde ExcepType este tipul de excepție care apare. Când o excepție este aruncată de o instrucțiune try, aceasta este prinsă de instrucțiunea catch asociată, care apoi se ocupă de excepție. În funcție de tipul de excepție, se execută instrucțiunea catch corespunzătoare. Deci, dacă tipurile de excepție generată și cea specificată în instrucțiunea catch se potrivesc, atunci această instrucțiune particulară este executată și toate celelalte sunt omise. Când o excepție este capturată, variabila de excepție exOb își primește valoarea. De fapt, nu este necesar să specificați variabila exOb. Astfel, nu este necesar să îl specificați decât dacă handlerul de excepție are nevoie de acces la obiectul excepție, ceea ce este destul de des cazul. Pentru a gestiona o excepție, tipul acesteia este suficient.

Rețineți, totuși, că, dacă nu se aruncă nicio excepție, blocul de instrucțiuni try se termină normal și oricare dintre instrucțiunile sale catch este omisă. Execuția programului se reia de la prima instrucțiune care urmează instrucțiunii finale catch. Astfel, instrucțiunea catch este executată numai dacă se aruncă o excepție.

Să ne uităm la un exemplu în care ne vom ocupa de o excepție care apare la împărțirea unui număr la 0:

Utilizarea sistemului; folosind System.Collections.Generic; folosind System.Linq; folosind System.Text; namespace ConsoleApplication1 ( clasa Program ( static int MyDel(int x, int y) ( return x / y; ) static void Main() ( try ( Console.Write("Enter x: "); int x = int.Parse(Console .ReadLine()); Console.Write("Introduceți y: "); int y = int.Parse(Console.ReadLine()); int rezultat = MyDel(x, y); Console.WriteLine("Rezultat: " + rezultat); ) // Gestionează o excepție care apare la împărțirea la zero captură (DivideByZeroException) ( Console.WriteLine("Diviziune cu 0 detectată!!!\n"); Main(); ) // Gestionează o excepție când un număr este introdus incorect în captura de consolă (FormatException) ( Console.WriteLine("Acesta NU este un număr!!!\n"); Main(); ) Console.ReadLine(); ) ) )

Acest exemplu simplu ilustrează în mod clar gestionarea unei excepții la împărțirea la 0 (DivideByZeroException), precum și o eroare de utilizator la introducerea unui non-număr (FormatException).

Consecințele neprinderii excepțiilor

Prinderea uneia dintre excepțiile standard, ca în exemplul de mai sus, are un alt beneficiu: elimină prăbușire programe. Odată ce o excepție este aruncată, aceasta trebuie să fie prinsă de o bucată de cod într-o anumită locație din program. În general, dacă o excepție nu este prinsă în program, atunci aceasta va fi capturată de sistemul de rulare. Dar ideea este că sistemul de execuție va emite un mesaj de eroare și va întrerupe execuția programului.

O excepție este o instanță a unei clase care a fost moștenită atât explicit, cât și implicit din clasa de bază System.Exception.

Variabilele declarate într-un bloc try ies din domeniul de aplicare atunci când controlul este transmis unui catch sau, în final, bloc. La sfârșitul blocului de încercare, chiar dacă nu s-a întâmplat nimic (nu a apărut nicio eroare), controlul este transferat automat către blocul final, care trebuie să conțină instrucțiuni pentru eliberarea resurselor.

Când este detectată o eroare, codul aruncă o excepție (creând o instanță a clasei de excepție) și o emite după cum urmează:

Throw new IndexOutOfRangeException ("Ați introdus: " + userInput);

De îndată ce compilatorul întâlnește o instrucțiune throw în interiorul unui bloc try, caută imediat un bloc catch corespunzător.

Instrucțiunile Throw nu trebuie să fie în aceeași metodă ca și blocul try; blocul try continuă să funcționeze chiar și atunci când controlul este transmis altor metode.

Instrucțiunea throw poate fi în orice metodă apelată în timpul execuției blocului try — nu trebuie să fie în aceeași metodă în care este definit blocul try.

Manipulatorii de excepții pentru clasa derivată (IndexOutOfRangeException) trebuie să fie înaintea celor pentru clasa de bază (Exception) (!)

Dacă un catch handler este scris ca: catch ( … ), aceasta înseamnă că este responsabil pentru orice cod (pentru orice excepție care apare), inclusiv pentru cele care nu sunt scrise în C# sau care nu sunt gestionate.

Este o cerință C# ca numai instanțe ale unei clase derivate din System.Exception să poată fi transmise ca excepții.

În timp ce (adevărat) // ciclu nesfârșit( Console.Write("Selectați un element de meniu de la 0 la 5 sau pentru a ieși: "); string userInput = Console.ReadLine(); if (userInput == "") (Console.WriteLine("Ați apăsat pe ieșire... "); break; ) încercați ( int x = int.Parse(userInput); comutați (x) (case 0: Console.WriteLine("Elementul 0 selectat"); break; caz 1: Console.WriteLine("Elementul 1 selectat "); pauză; caz 2: Console.WriteLine("Elementul 2 selectat"); pauză; caz 3: Console.WriteLine("Elementul 3 selectat"); pauză; caz 4: Console.WriteLine("Elementul 4 selectat") ; pauză; cazul 5: Console.WriteLine("Elementul 5 este selectat"); pauză; implicit: aruncați un nou IndexOutOfRangeException(); break; ) ) catch (IndexOutOfRangeException e) ( Console.WriteLine("Valoare nevalidă. Selectați o cifră din ") ; ) catch (FormatException e) ( Console.WriteLine("Eroare de conversie, este posibil să fi introdus un șir..."); ) catch (Excepție e) ( Console.WriteLine("O altă eroare"); ) catch ( ) // gestionează codul negestionat și codul într-o altă limbă)

Proprietăți și metode ale sistemului.Excepție

if (ErrorCondition == true) ( ​​​​Excepție myException = new ClassMyException ("Ajutor!"); myException.Source = "Numele aplicației"; myException.HelpLink = "myHelpFile.txt"; aruncați myException; )

Codul nu trebuie să arunce excepții din clasa generală System.Exception - nu oferă nicio idee despre natura stării de eroare (!). Există două clase importante în ierarhie:

SystemException - Destinat pentru excepții aruncate de obicei de runtime .NET sau excepții care sunt de natură generală și pot fi aruncate de aproape orice aplicație. Subclasa reprezintă atât erorile fatale, cât și erorile nefatale.

ApplicationException - este o bază destinată oricărei clase de excepții definite de terți.

IOException (spațiul de nume System.IO) sunt asociate cu citirea și scrierea datelor într-un fișier.

StackOverflowException apare atunci când memoria alocată stivei este umplută la capacitate maximă. O depășire a stivei poate apărea, de exemplu, atunci când o metodă începe să se autoapeleze recursiv. O cauză comună a unei excepții EndOfStreamException este o încercare de a citi dincolo de limitele unui fișier. O excepție OverflowException apare, de exemplu, când încercați să turnați un int care conține -40 la un uint într-un context verificat.

Durerile de încercare imbricată sunt folosite din două motive:

— pentru a modifica tipul excepției generate (folosind proprietatea InnerException, care conține o referință la orice excepție generată);

— gestionarea diferitelor excepții în diferite părți ale codului.

Definirea propriilor clase de excepție

Definirea propriilor excepții este o sarcină destul de simplă, deoarece... rareori trebuie să le adăugați propriile metode. Este necesar doar să implementați constructorul pentru a vă asigura că constructorul clasei de bază este apelat corect.

Clasa LandLineSpyFoundException: ApplicationException ( public LandLineSpyFoundException(string spyName) : base("spyName"+spyName) ( ) public LandLineSpyFoundException(string spyName, Exception innerException) : base(spyName, innerException) ( ) )

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

Aș dori să remarc imediat că erorile din programe pot fi împărțite în două grupe: erori de asamblare a programelor (la etapa de compilare) și erori de rulare a programului. Acest tutorial va vorbi despre erorile de rulare!

Să ne amintim exemplul din lecția anterioară, în care utilizatorul trebuia să introducă două numere întregi de la tastatură. Apoi am cerut și să introduc numere și numere întregi. De ce? Înainte pentru că dacă introduceți î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, astăzi vă vorbesc despre mecanismele și principiile de tratare a erorilor în programe.

Și astfel, 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 procesării situatii similare este urmând reguli: este evidențiat un bloc de cod, a cărui execuție poate provoca erori; este alocat un bloc de cod care este responsabil pentru procesarea erorilor care au apărut.

Este indicat un bloc de cod în care pot apărea erori cuvânt cheie încerca urmată de bretele(care pot conține operațiuni periculoase). Imediat după un astfel de bloc ar trebui să existe un bloc de gestionare a erorilor; acesta este notat cu cuvântul cheie captură, iar în paranteze după acest cuvânt, tipul de erori pe care blocul de date al codului le prelucrează după închidere paranteze, urmate de cele crete, în cadrul cărora se implementează 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 se execută, dar se transmite controlul, adică se execută următorul bloc captură operator. Dar dacă în interiorul blocului încerca apare o eroare, 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 erorilor de diferite tipuri.

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

//Încercare de blocare potențial periculoasă ( //Solicitați utilizatorului să introducă primul număr Console.Write("Introduceți primul număr și apăsați Introduce cheia: "); //Obținerea primului șir de linie firstString = Console.ReadLine(); //Conversia primei linii într-un număr int firstArg = Convert.ToInt32(firstString); //Solicitarea utilizatorului să introducă al doilea număr Console. Write("Introduceți primul număr și apăsați tasta Enter: "); //Obținerea celui de-al doilea șir de linie secondString = Console.ReadLine(); //Convertirea celei de-a doua linie într-un număr int secondArg = Convert.ToInt32(secondString); //Adăugarea a două variabile int result = firstArg + secondArg ; //Ieșire rezultatul Console.WriteLine("Rezultatul adăugării numerelor introduse: " + result.ToString()); ) //Blocul de gestionare a erorilor, SystemException - cel mai mare tip obișnuit de erori (SystemException) ( Console.WriteLine ("În timpul rulării a apărut o eroare de program, probabil că au fost introduse date incorecte!"); )

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

Și așa, în această lecție a fost prezentat informatii de baza privind mecanismul de tratare a erorilor (situații de excepție) în limbajul C#. În următoarea lecție, vom vorbi despre crearea și utilizarea propriilor metode (funcții) și vom reveni la subiectul gestionării erorilor de mai multe ori!

Cele mai bune articole pe această temă