'Y'

ErrorProne.NET partea III

Astazi vreau sa vorbesc despre una dintre cele mai noi caracteristici ErrorProne.Net si in urmatorul articol vom discuta despre cum este ea implementata.

May 19, 2016 1670
Astazi vreau sa vorbesc despre una dintre cele mai noi caracteristici ErrorProne.Net si in urmatorul articol vom discuta despre cum este ea implementata.

In C# sunt multe caracteristici care apar sub forma unui cod dificil IL si care duc la comportamente care nu sunt intotdeauna evidente pentru utilizatori. Un exemplu bun al acestui tip de comportament este excepetia new() in generalizari, a carei utilizare duce la folosirea reflection si dezvoltarea de noi obiecte cu Activator.CreateInstance, schimband astfel profilul exceptiei si afectand in mod negativ performanta.

In afara de asta, mai sunt cateva caracteristici cu implementari similare si efecte curioase in ceea ce priveste procesarea exceptiilor.

Preconditii in interator block

Compilerul C# transforma interator block intr-un instrument automat care produce comportamente recunoscute pe scara larga in unele cercuri ca fiind continuation passing style. Nu este nimic complicat in constructiile in sine, dar probleme pot avea loc atunci cand sunt folosite in mod incorect.

Sa luam in considerare un asemenea exemplu, desi este destul de primitiv.

ErrorProne.NET. Part 3_1.jpg

Nu este perfect dar in mod cert se poate intampla. La inceputul metodei avem validarea argumentelor si apoi deschidem fisierul si ii citim continutul linie cu linie. Principala problema este timpul de care este nevoie pentru a genera exceptii. Cat de evident este, la prima vedere, cand va avea loc?

ErrorProne.NET. Part 3_2.jpg

Din moment ce iterator block nu este o metoda comuna, exceptia va avea loc doar atunci cand iterator se materializeaza, asa va reiesi in linia 3. Din moment ce iterator block este executat alene, precondition check va fi facuta doar la prima apelare a metodei MoveNext pe iteratorul obtinut. Acest lucru nu este rau atunci cand avem doar o linie de cod intre obtinerea iterator si utilizarea lui astfel nu va fi foarte dificil sa intelegem motivul initial. Dar in practica, IEnumerable<T> poate fi salvat sau transferat intr-un alt subsistem, astfel ca este destul de dificil sa identificam aceasta informatie.

Problema este rezolvata destul de tipic: metoda are nevoie sa fie impartita in doua, prima este lasata ca precondition check si principala sarcina este transferata la metoda separata.

ErrorProne.NET. Part 3_3.jpg

Nu este dificil sa ne dam seama ca ErrorProne.Net are o regula speciala pentru identificarea unor asemenea cazuri si un fixer care poate imparti metoda in doua.

Preconditii in metode asynchronous

Preconditii in iterator blocks pot sa para destul de exotice, dar aceeasi problema este evidenta si in alte structuri de limbaje - metode asynchronous adica metode identificate the keyword async.

Orice metoda contine un contract formal sau informal. Daca cererea indeplineste o anumita conditie denumita preconditie, metoda va face tot ce ii sta in putinta pentru a-si indeplini obligatiile (cum ar fi sa incerce sa-si garanteze post conditiile). Luand in considerare timpul necesar pentru indeplinire, incalcarea preconditiilor este in general modelata folosind ArgumentException (sau clase derivate) in timp ce alte exception types modeleaza problemele de implementare si pot fi interpretate ca o incalcare a post conditiilor.

Exclusion type si timpul de indeplinire ne permit sa intelegem mai bine responsabilitatile diferitelor componente si sa ne dam seama ce nu a mers bine si ce trebuie sa facem. Insa cand vine vorba de metode asynchronous totul devine un pic mai dificil.

Cu aparitia TAP (Task-based async pattern), metoda a obtinut 2 moduri prin care sa ne informeze despre o problema. Metoda poate sa afiseze o excludere cand o apelam sau sa arata ca sarcina este broken. Precondition violation exclusions sunt sincronice si informeaza method client despre faptul ca obligatiile nu au fost indeplinite. Exclusion synchronism anunta ca a gresit si ca operatia nici nu a inceput. O sarcina broken indica ca a existat o problema in indeplinirea sarcinilor in termeni de implementare.

Acum sa ne intoarcem la metoda asynchronous.

ErrorProne.NET. Part 3_4.jpg

In ce moment va aparea ANE exclusion?

ErrorProne.NET. Part 3_5.jpg

Metodele asynchronous sunt facute in asa fel incat partea din metoda care se afla inainte de primul await este efectuata sincrona, dar daca are loc o excludere, nu va fi trimisa catre client direct. In schimb metoda va fi finalizata cu succes si excluderea va fi redirectionata la momentul in care acest rezultat este primit de la sarcina.

Provocarea legata de un asemenea comportament este aceeasi ca in cazul interator block. Putem sa obtinem o sarcina, sa o salvam in cadru campului, sa o transferam catre o alta metoda si sa observam rezultatul ArgumentNullException in partea opusa a programului. Insa, de fapt, sarcina nici macar nu a fost rulata deoarece preconditiile nu au fost indeplinite.

In acest caz, acelasi truc de method allocation este folosit: impartim metoda in doua, stergem async keyword din prima parte dar lasam precondition check si alocam restul catre metoda auxiliara.

ErrorProne.NET. Part 3_6.jpg

Tine mereu minte regulile

In trecut de fiecare data cand schimbam echipele unul din primele lucruri pe care le faceam era sa refac regulile de programare deoarece in majoritatea cazurilor nu erau incluse practicile utile de procesare a exceptiilor. Acum ca avem la dispozitie analizatori utili, majoritatea regulilor pot fi usor automatizate si compilarea poate fi intrerupta cand regulile nu sunt indeplinite.

(Stiu despre regulile personalizate FxCop dar nu am vazut niciodata reguli personalizate pentru procesarea corecta a exceptiilor. Mai mult, FxCop nu este intretinut de cineva in mod specific, asa ca nimeni nu poate garanta ca va fi compatibil cu ultima versiune de compiler, deoarece foloseste nivelul de analiza IL, unde este foarte greu sa analizezi metode asynchronous.)

Preconditii si contracte

Toate sugestiile mele despre preconditii / post conditii sunt confirmate de faptul ca instrumentele pentru design by contract, adica Code Contracts, proceseaza interator block si preconditiile metodei asynchronous intr-un mod specific. Efectueaza aceleasi conversii logice explicate aici: si anume fac ca incalcarile preconditiilor sa se activeze in mod sincron si sa fie afisate catre client in momentul in care metoda este apelata si nu in momentul in care rezultatul este procesat!


Sergey Teplyakov
Expert in .Net, ++ and Application Architecture

Daca iti place acest articol, distribuie-l si prietenilor tai!




Mai ai intrebari?
Contacteaza-ne.
Thank you.
Your request has been received.