Sunday, June 16, 2019 04:48

Primii pași în Debugging: înțelegerea erorilor comune

Așa cum am spus deja de mai multe ori, depanarea, sau debugging-ul, este procesul de corectare a erorilor în codurile programelor dvs. Acest lucru este doar parțial adevărat. Depanarea este și despre procesul de înțelegere a erorii și a motivului pentru care eroarea a existat în primul rând. Lecția de astăzi va fi despre această parte a depanării.

Mulți programatori începători, dornici să înceapă scrierea programelor cât mai curând posibil, adesea ignoră lecțiile de depanare, fără să-și dea seama că 2 ore petrecute învățând despre procesul de depanare îi pot ajuta să salveze sute de ore de corectare a erorilor ulterioare. Și sfârșesc de cele mai multe ori pe forumurile de programare, disperați după ce petrec zile de încercări zadarnice în încercarea de a mai pune un petic peste programele lor deja peticite, punând întrebări pe care o lecție anterioară de depanare le-ar fi făcut inutile, sau pur și simplu ajungând la fundul deznădejdii de programator: „nu merge, și nu știu de ce! Puteți să scrieți codurile în locul meu? „. Posibil ca aceștia să rămână surprinși să constate că cei mai experimentați programatori se încruntă asupra unor astfel de solicitări și de cele mai multe ori doar critică cererile din această categorie, sau pur și simplu sar la următoarea întrebare, scuturând capul în dezaprob, chiar dacă știu răspunsul. Motivul pentru acest lucru nu este acela că programatorii devin niște creaturi extraterestre anti-sociale, care visează coduri și formule și privesc de sus la ceilalți muritori din jurul lor. Programatorii seniori au început și ei, probabil, sărind peste tutorialele de depanare și au trecut prin același iad de vânătoare pe întuneric a erorilor. Diferența constă în faptul că au înțeles curând că modul corect de a învăța cum să scrie programe robuste și să remedieze erorile care apar nu este de a cere coduri pe forumuri și de a fi fericiți că au reușit să îi păcălească pe ceilalți să scrie codul în locul lor, pentru că aceasta este la fel de inutil ca și încercarea de a transporta apă cu o plasă. Data viitoare când vor întâlni o eroare, vor fi la fel de neștiutori, enervând și pierzând și mai mult din timpul altor programatori. În schimb, ei au realizat că problema este însăși plasa, care trebuie reparată sau înlocuită cu un alt obiect, mai potrivit pentru căratul apei. Ei au înțeles că problema are legătură cu ei înșiși, și ei sunt cei care trebuie să înceteze să mai solicite altora să le corecteze codurile, și să înceapă de fapt să învețe modul corect de a remedia erorile.

Cu alte cuvinte, dați unui om un pește și el va mânca o zi. Învățați un om să pescuiască și el va mânca pentru o viață. Și pe bune dacă înțeleg de ce nicio universitate nu oferă cursuri de debugging. Suficient să spun că oricine învață să depaneze corect este cu  un cap deasupra colegilor de clasă. Oricum, haide să începem.

Dacă vă mai amintiți încă dintr-o lecție anterioară, am spus că un program poate exista în trei stări la un moment dat: timpul de proiectare (atunci când scriem codurile noastre), timpul de execuție (atunci când compilăm și executăm codurile noastre) și timpul de depanare (activat fie în mod automat, când apare o excepție, fie manual, când intrăm în mod specific în modul de depanare, așa cum vom învăța mai târziu).

Primul pas în procesul de depanare este identificarea locului în care apare eroarea, bucata de cod care generează eroarea. Din fericire, cu excepția cazurilor foarte speciale, Visual Studio face deja o treabă minunată în acest sens, oprind execuția și evidențiind automat rândul care produce eroarea în codurile noastre. Probabil ați văzut deja acest lucru de câteva ori:

System.NullReferenceException

Următorul lucru pe care ar trebui să-l faceți este să deschideți filele Locals și Autos. Știu că nu am mai vorbit niciodată despre ele înainte, dar sunt critice în procesul de depanare. Dacă acestea nu sunt deja deschise, puteți face acest lucru accesând meniul Debug, submeniul Windows și selectând Autos și Locals. Rețineți că aceste meniuri sunt disponibile numai atunci când vă aflați în modul de debugging, despre care vom învăța mai târziu cum puteți să îl activați.

Visual Studio Autos si Locals

Când vă aflați în modul de depanare, aceste două ferestre vor apărea ca panouri în partea de jos a Visual Studio:

Aceste două panouri vă permit să vedeți valorile variabilelor în domeniul de definiție curent, în timp real. Mai târziu, veți afla că analiza rapidă a valorilor variabilelor în aceste file poate de cele mai multe ori să vă îndrepte în direcția corectă de rezolvare a erorilor. La fel ca în exemplul de mai sus, când am încercat să execut codul respectiv, compilatorul a oprit execuția, a intrat în modul de depanare și a aruncat o excepție de tip System.NullReferenceException. Acest lucru îmi oferă deja un mic indiciu despre ceea ce este greșit în codurile mele; și chiar dacă nu, o căutare rapidă pe Google după numele erorii îmi poate oferi acest indiciu: „o excepție *null reference*. Ei bine, asta înseamnă că este ceva legat de null, sau mai precis, de ceva ce are valoarea nul, când ar trebui să fie altceva”. Pentru că, desigur, încercarea de a folosi o valoare nulă este imposibilă și va genera o eroare, exact eroarea pe care am primit-o. Deci, Visual Studio m-a ajutat să găsesc locația erorii mele, oprind executarea și evidențiind linia de eroare cu galben, apoi mi-a oferit o idee generală despre ce anume este eroarea respectivă. Acest lucru a redus deja complexitatea problemei mele câteva ordine de magnitudine. Este suficient acum doar să arunc o privire rapidă la filele Locals și Autos și să văd valorile variabilelor mele, și deja am un moment „Aha!”: variabila text este nulă, așa cum puteți vedea în imagine, iar eu încerc să folosesc metoda ToUpper() pe o valoare nulă, pe o valoare care nu există! Deci, păstrați întotdeauna un ochi pe aceste două tab-uri, s-ar putea să vă economisiți o mulțime de timp văzând că variabila dvs. este 0, atunci când ar fi trebuit să fie 158.

De asemenea, trebuie să știți că o funcționalitate similară poate fi obținută doar prin plasarea mouse-ului peste variabilele dvs. (din nou, rețineți că acest lucru se întâmplă numai în modul de debugging și numai în domeniul de definiție actual al execuției, cu alte cuvinte, în domeniul de definiție al blocului unde a apărut eroarea). Deci, puteți fie să aruncați o privire la filele Autos și Locals, fie puteți să treceți cu mouse-ul peste variabile, până la actualizarea tooltip-ului. Astfel, pot vedea rapid că variabila text are o valoare nulă:

variabila nula, debugging

Să luăm un alt tip de eroare comună, pe care mulți începători o întâmpină adesea: Object {name} is not set to the instance of an object. Doamne, dacă ați ști cât de mult m-a bântuit această eroare când doar ce învățam conceptele de bază ale programării orientată pe obiecte! Și încă îmi amintesc că eram atât de supărată și fără habar despre ceea ce înseamnă! Să luăm un exemplu:

Vedeți ceva greșit în codul de mai sus? Ei bine, acesta este exact genul de cod care vă va face să intrați în necazul erorii de mai sus. Partea bună este faptul că am învățat deja cum să corectăm acest gen de erori. Da, da, chiar am făcut-o!

Vă amintiți de lecția despre clase și instanțiere? Am spus acolo că atunci când lucrăm cu obiecte (clase), nu vom folosi niciodată schița în sine, ci vom crea copii după ea (dacă nu dorim să creem obiecte statice). Începeți să vedeți problema în codul de mai sus? Nu? Bine, haide să ne gândim logic. Am declarat un obiect de tip Button. Nimic special până aici. Dar l-am instanțiat, de fapt? Am creat o copie a schiței Button? Nu! Variabila butonulMeu nu a fost niciodată setată la o instanță a ceva, și este încă nulă! Adevărat, este o variabilă de tip Button, dar nu este o copie a unui buton! Deci, să creem această copie:

Rulați codul de mai sus și veți vedea excepția dispărând! De ce? Pentru că i-am spus compilatorului că nu vrem să modificăm clasa originală Button, ci să creăm o copie a acesteia și să manipulăm această copie, ceea ce este complet permis.

Să luăm un alt tip de eroare foarte frecventă în rândul începătorilor, stack overflow (depășirea stivei). Luați în considerare următorul cod:

Ceva ciudat la codul de mai sus? Nu am învățat despre zonele de memorie Heap și Stack încă, dar nici nu e neapărat necear. Aruncând o privire rapidă la codul de mai sus, putem spune că ceva este FOARTE greșit: metodaUnu() apelează metodaDoi(), care apelează metodaUnu(), care apelează metodaDoi(), etc, etc, ați prins idea. Aceasta este o buclă infinită de apelare. Trebuie să știți pentru moment doar că ori de câte ori apelăm o funcție sau o metodă, acest apel este stocat într-o listă într-o zonă de memorie specială numită Stack. Dar această memorie Stack are o dimensiune foarte mică, nu poate înregistra un număr infinit de apeluri în lista sa. Prin urmare, va depăși și vom ajunge la excepția în cauză. Deci, dacă obțineți această eroare, să știți că undeva, o parte din codul vostru face prea multe apeluri la o anumită metodă.

Cannot apply mathematical operation to string. (Nu se poate aplica operația matematică la șirul de caractere). Iată o eroare pe care începătorii o întâlnesc ori de câte ori încearcă să lucreze cu numere și matematică. După cum ar trebui să știți deja, de cele mai multe ori vom primi datele de la utilizatorii noștri ca text. Deci, chiar dacă folosim Console.Read(), chiar dacă luăm valoarea textului dintr-un control de tip textbox într-o aplicație GUI, vom obține acea valoare tot ca text! Chiar dacă utilizatorul apasă tasta „5”, 5 este citită drept litera 5, nu numărul 5! Știu că uneori este destul de greu de înțeles acest concept, dar textul 5 și numărul 5 sunt două lucruri diferite. Pentru că:

Folosind șiruri de caractere, ajungem să le concatenăm (unim), în timp ce folosind numere, ajungem să facem operații aritmetice. Așadar, amintiți-vă, ÎNTOTDEAUNA trebuie să vă convertiți șirurile sau intrările de la utilizatori în numere, dacă doriți să aplicați operații matematice pe acestea. Știu că vă gândiți „ce tâmpenie, să ai o semnificație separată pentru textul 5 și numărul 5!”, Dar răspundeți la o singură întrebare: ce s-ar întâmpla dacă utilizatorul ar tasta „scooby doo” în loc de „5” ca input al programului? Tot ar fi valabil pentru aritmetica? Nu este vina utilizatorului că tastatura îi permite să introducă atât cifre, cât și litere.. În concluzie, întotdeauna convertiți șirurile de caractere numerice în cifre, după ce vă asigurați mai întâi că acestea sunt de fapt reprezentarea textuală a numerelor (și nu scooby doo!).

The object {name} does not exist in the given context (Obiectul {nume} nu există în contextul dat). O altă eroare care m-a făcut să-mi smulg părul atunci când eram începătoare, când în realitate era de fapt un concept atât de simplu! Această eroare se referă la domeniul de definiție și durata de viață a variabilelor noastre:

Așa cum am învățat în lecția domeniul de definiție al variabilelor, o variabilă este „în viață” doar atât timp cât blocul în care a fost declarată este încă în viață. Dacă execuția a părăsit respectivul bloc, variabila a dispărut. În exemplul de mai sus, doar pentru că am declarat picnic.cos nu înseamnă că Urs știe ceva despre el, deoarece el nu face parte din acea clasă. E un lucru trist când nu ești invitat la picnic!

Not all code paths return a value. (Nu toate căile de cod returnează o valoare). Aceasta este o altă eroare simplă. Chiar și eroarea în sine este auto-descriptivă, dar totuși îi tulbură pe mulți începători. În lecția funcții, am spus că o funcție trebuie să returneze întotdeauna o valoare, de tipul care a fost declarat în semnătura funcției. Cu alte cuvinte, dacă avem:

În acest exemplu, semnătura functiaMea spune că trebuie să returneze un int. Și am făcut-o. Dar ce s-ar întâmpla dacă am face asta, în schimb?

Va fi în continuare totul în regulă? Ei bine, NU! Ce se întâmplă dacă variabila ziuaSaptamanii NU este luni? Care este valoarea returnată în acest caz? Niciuna… Și deoarece funcția TREBUIE să returneze o valoare int, acest lucru generează o eroare. Trebuie să vă asigurați întotdeauna că o funcție returnează o valoare, indiferent de condițiile calculelor voastre.

Index was outside the bounds of the array/collection. (Indexul a fost în afara limitelor array-ului/colecției). Am vorbit deja despre această eroare când am învățat despre array-uri. Și ar trebui să știți deja ce urmează să spun: dacă avem o colecție, orice fel de colecție, cu de exemplu, 5 elemente, și încercăm să accesăm al șaptelea element, cum ar fi posibil acest lucru? Amintiți-vă întotdeauna limitele colecțiilor voastre atunci când primiți această eroare și amintiți-vă întotdeauna că majoritatea indicilor încep de obicei la 0, nu 1!

Deci, după ce am vorbit despre lucruri foarte generale de depanare și tipuri de erori comune, în următoarea lecție vom începe în sfârșit să învățăm totul în profunzime.

Comments

comments

Tags:

Leave a Reply