Folosirea excepțiilor nefolosite în loc de Conține ()?

Imaginați-vă că un obiect cu care lucrați are o colecție de alte obiecte asociate cu acesta, de exemplu, colecția de controale de pe WinForm. Doriți să verificați un anumit obiect din colecție, dar colecția nu are o metodă Contains() . Există mai multe modalități de abordare a acestui fapt.

  • Implementați-vă propria metodă Contains() prin reajustarea tuturor elementelor din colecție pentru a vedea dacă unul dintre ele este ceea ce căutați. Aceasta pare a fi abordarea "celor mai bune practici".
  • Am întâlnit recent un cod în care, în loc de o buclă, a avut loc o încercare de a accesa obiectul în interiorul unei instrucțiuni de încercare, după cum urmează:
  încercați
{
    Obiect aObject = MyCollection [myObject];
}
captură (excepție e)
{
    // dacă este aruncat, atunci obiectul nu există în colecție
}
 

Întrebarea mea este cât de săracă a unei practici de programare considerați a doua opțiune să fie și de ce? Cum este performanța acesteia comparată cu o buclă prin colecție?

0
fr hi bn

8 răspunsuri

Regula generală este de a evita utilizarea excepțiilor pentru fluxul de control, cu excepția situațiilor în care circumstanțele care vor declanșa excepția sunt "excepționale" - de exemplu extrem de rare!

Dacă acesta este ceva ce se va întâmpla în mod normal și în mod regulat, cu siguranță nu ar trebui să fie tratat ca o excepție.

Excepțiile sunt foarte, foarte lentă datorită tuturor cheltuielilor implicate, astfel încât pot exista și motive de performanță, dacă se întâmplă destul de des.

0
adăugat

Ar trebui să mă gândesc mai mult la cât de mult îmi place ... instinctul meu intestinal este, nu-i așa ...

EDIT: Comentariile lui Ryan Fox despre cazul excepțional sunt perfecte, concur

În ceea ce privește performanța, depinde de indexatorul din colecție. C# vă permite să înlocuiți operatorul de indexare, deci dacă se face o buclă de buzunar ca și metoda pe care ați scrie-o, atunci va fi la fel de lentă (poate cu câteva nanosecunde mai lent din cauza încercării/capturii ... dar nimic de vă faceți griji cu excepția cazului în care codul în sine se află într-o buclă imensă).

Dacă indexatorul este O (1) (sau chiar O (log (n)) ... sau ceva mai rapid decât O (n)), atunci soluția try/catch ar fi mai rapidă.

De asemenea, presupun că indexerul aruncă excepția, dacă se întoarce nul, ați putea, desigur, să verificați doar pentru null și să nu folosiți încercarea/captura.

0
adăugat

Excepțiile trebuie să fie excepționale.

Ceva ca "Colecția lipsește deoarece baza de date a căzut de sub ea" este excepțională

Ceva ca "cheia nu este prezent" este un comportament normal pentru un dicționar.

Pentru exemplul dvs. specific al colecției Winforms Control, proprietatea Controls are o metodă ContainsKey , pe care ar trebui să o utilizați.

Nu există niciun ContainsValue deoarece, atunci când se ocupă de dicționare/hashtables, nu există o cale rapidă de a itera întreaga colecție, de a verifica dacă este prezent ceva, deci sunteți cu adevărat descurajați să faceți asta.

În ceea ce privește DE CE Excepțiile ar trebui să fie excepționale, este vorba despre 2 lucruri

  1. Indică ce cod încearcă să facă. Doriți ca codul dvs. să corespundă ceea ce încearcă să realizeze cât mai aproape posibil, astfel încât să poată fi citit și întreținut. Manipularea excepțiilor adaugă o grămadă de crustă extra care se opune acestui scop

  2. Brevetarea codului. Vreți ca codul dvs. să facă ceea ce face în cel mai direct mod, astfel încât acesta să poată fi citit și întreținut. Din nou, cruftul adăugat prin manipularea excepțiilor devine astfel.

0
adăugat

Dacă, în timp ce scrieți codul dvs., vă așteptați ca acest obiect să fie în colecție și apoi în timpul rulării veți găsi că nu este, aș numi un caz excepțional și este bine să folosiți o excepție.

Cu toate acestea, dacă încercați pur și simplu pentru existența unui obiect și veți găsi că nu există, acest lucru nu este excepțional. Utilizarea unei excepții în acest caz nu este adecvată.

Analiza performanței runtime depinde de colecția efectivă utilizată și de metoda căutării. Dar asta nu ar trebui să conteze. Nu lăsați iluzia optimizării să vă păcălească în scrierea unui cod confuz.

0
adăugat

Ar trebui să spun că este o practică destul de rău. În timp ce unii oameni ar putea fi fericiți să spună că prin looping prin colecție este mai puțin eficientă să aruncați o excepție, există o cheltuială care aruncă o excepție. De asemenea, aș fi întrebat de ce folosești o colecție pentru a accesa un element după cheie atunci când ar fi mai potrivit să folosești un dicționar sau un hashtable.

Principala mea problemă cu acest cod, totuși, este că, indiferent de tipul de excepție aruncat, veți rămâne întotdeauna cu același rezultat.

De exemplu, o excepție ar putea fi aruncată deoarece obiectul nu există în colecție, sau deoarece colecția însăși este nulă sau pentru că nu puteți arunca myCollect [myObject] la aObject.

Toate aceste excepții vor fi tratate în același mod, care nu ar putea fi intenția dvs.

Acestea sunt câteva articole frumoase cu privire la momentul și locul în care se consideră acceptabil să se facă excepții:

Îmi place foarte mult acest citat din al doilea articol:

Este important ca excepțiile să fie   aruncat numai atunci când un neașteptat sau   se produce o activitate nevalidă care împiedică   o metodă de a-și termina normalitatea   funcţie. Excepție de manipulare   introduce o mică deasupra capului și coboară   performanță, astfel încât nu ar trebui să fie utilizate pentru   program normal în loc de   prelucrare condiționată. De asemenea, poate fi   dificil de a menține codul   abuzează de tratarea excepțiilor în acest sens   mod.

0
adăugat

Acesta din urmă este o soluție acceptabilă. Deși mi-ar prinde cu siguranță excepția specifică (ElementNotFound?) Pe care o colectează colecția în acest caz.

În funcție de viteză, depinde de cazul comun. Dacă sunteți mult mai probabil să găsiți elementul decât nu, soluția de excepție va fi mai rapidă. Dacă ești mai probabil să eșuezi, atunci ar depinde de mărimea colecției și de viteza de iterație. În orice caz, doriți să măsurați împotriva utilizării normale pentru a vedea dacă acesta este de fapt un gât de sticlă înainte de a vă îngrijora de viteza de genul asta. Du-te mai întâi de claritate, iar ultima soluție este mult mai clară decât cea dintâi.

0
adăugat

Take a look at this blog post from Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

Ar trebui folosite excepții pentru comunicarea condițiilor de eroare, dar nu ar trebui folosite ca logică de control (mai ales atunci când există moduri mult mai simple de a determina o condiție, cum ar fi Conține).

O parte din problemă este că excepțiile, în timp ce nu este costisitoare să aruncați sunt costisitoare pentru capturați și toate excepțiile sunt prinse la un moment dat.

0
adăugat

În general, utilizarea gestionării excepțiilor pentru fluxul de programe și logica este o practică nepotrivită. Personal, consider că această din urmă opțiune este o utilizare inacceptabilă a excepțiilor. Având în vedere caracteristicile limbilor utilizate frecvent în aceste zile (cum ar fi Linq și lambda în C#, de exemplu) nu există nici un motiv să nu scrieți propria metodă Contains ().

Ca un gând final, în zilele noastre majoritatea colecțiilor do au o metodă deja conține. Deci, cred că în cea mai mare parte aceasta este o problemă.

0
adăugat