Frage

Stellen Sie sich vor, mit einem Objekt, mit dem Sie arbeiten, ist eine Sammlung anderer Objekte verknüpft, beispielsweise die Controls-Sammlung auf einem WinForm.Sie möchten nach einem bestimmten Objekt in der Sammlung suchen, die Sammlung verfügt jedoch nicht über ein Objekt Contains() Methode.Es gibt mehrere Möglichkeiten, damit umzugehen.

  • Implementieren Sie Ihr eigenes Contains() Methode, indem Sie alle Elemente in der Sammlung durchlaufen, um zu sehen, ob eines davon das ist, wonach Sie suchen.Dies scheint der „Best Practice“-Ansatz zu sein.
  • Kürzlich bin ich auf einen Code gestoßen, in dem anstelle einer Schleife versucht wurde, innerhalb einer try-Anweisung auf das Objekt zuzugreifen, und zwar wie folgt:
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}

Meine Frage ist, wie schlecht die Programmierpraxis Ihrer Meinung nach die zweite Option ist und warum?Wie ist die Leistung im Vergleich zu einer Schleife durch die Sammlung?

War es hilfreich?

Lösung

Ich muss sagen, dass das eine ziemlich schlechte Praxis ist.Während einige Leute gerne sagen, dass das Durchlaufen der Sammlung weniger effizient ist als das Auslösen einer Ausnahme, ist das Auslösen einer Ausnahme mit einem Mehraufwand verbunden.Ich würde mich auch fragen, warum Sie eine Sammlung verwenden, um über einen Schlüssel auf ein Element zuzugreifen, wenn Sie besser für die Verwendung eines Wörterbuchs oder einer Hashtabelle geeignet wären.

Mein Hauptproblem bei diesem Code besteht jedoch darin, dass Sie unabhängig von der Art der ausgelösten Ausnahme immer das gleiche Ergebnis erhalten.

Beispielsweise könnte eine Ausnahme ausgelöst werden, weil das Objekt nicht in der Sammlung vorhanden ist, weil die Sammlung selbst null ist oder weil Sie myCollect[myObject] nicht in aObject umwandeln können.

Alle diese Ausnahmen werden auf die gleiche Weise behandelt, was möglicherweise nicht Ihre Absicht ist.

Dies sind ein paar nette Artikel darüber, wann und wo es normalerweise als akzeptabel angesehen wird, Ausnahmen auszulösen:

Dieses Zitat aus dem zweiten Artikel gefällt mir besonders gut:

Es ist wichtig, dass Ausnahmen nur dann abgeworfen werden, wenn eine unerwartete oder ungültige Aktivität auftritt, die verhindert, dass eine Methode ihre normale Funktion ausfüllt.Die Ausnahmebehandlung führt zu einem kleinen Overhead und senkt die Leistung. Daher sollte nicht für den normalen Programmfluss anstelle einer bedingten Verarbeitung verwendet werden.Es kann auch schwierig sein, Code beizubehalten, der auf diese Weise die Ausnahmebehandlung missbraucht.

Andere Tipps

Die allgemeine Faustregel besteht darin, die Verwendung von Ausnahmen für den Kontrollfluss zu vermeiden, es sei denn, die Umstände, die die Ausnahme auslösen, sind „außergewöhnlich“ – z. B. extrem selten!

Wenn dies normal und regelmäßig vorkommt, sollte es auf keinen Fall als Ausnahme behandelt werden.

Ausnahmen sind aufgrund des damit verbundenen Overheads sehr, sehr langsam, sodass es auch Leistungsgründe geben kann, wenn sie oft genug auftreten.

Wenn Sie beim Schreiben Ihres Codes erwarten, dass sich dieses Objekt in der Sammlung befindet, und dann während der Laufzeit feststellen, dass dies nicht der Fall ist, würde ich das als Ausnahmefall bezeichnen und es ist angemessen, eine Ausnahme zu verwenden.

Wenn Sie jedoch lediglich die Existenz eines Objekts testen und feststellen, dass es nicht vorhanden ist, ist dies keine Ausnahme.Die Verwendung einer Ausnahme ist in diesem Fall nicht angemessen.

Die Analyse der Laufzeitleistung hängt von der tatsächlich verwendeten Sammlung und der Methode zur Suche danach ab.Das sollte allerdings keine Rolle spielen.Lassen Sie sich nicht von der Illusion der Optimierung dazu verleiten, verwirrenden Code zu schreiben.

Ich müsste mehr darüber nachdenken, wie sehr es mir gefällt ...Mein Bauchgefühl ist, äh, nicht so sehr...

BEARBEITEN:Ich stimme zu, dass die Kommentare von Ryan Fox zu diesem Ausnahmefall perfekt sind

Die Leistung hängt vom Indexer der Sammlung ab.Mit C# können Sie den Indexer-Operator überschreiben. Wenn es also eine for-Schleife wie die Methode „contains“ ausführt, die Sie schreiben würden, ist es genauso langsam (vielleicht ein paar Nanosekunden langsamer aufgrund der Try/Catch-Funktion).aber kein Grund zur Sorge, es sei denn, der Code selbst befindet sich in einer großen Schleife.

Wenn der Indexer O(1) (oder sogar O(log(n)) ist ...oder irgendetwas schnelleres als O(n)), dann wäre die Try/Catch-Lösung natürlich schneller.

Außerdem gehe ich davon aus, dass der Indexer die Ausnahme auslöst. Wenn er null zurückgibt, können Sie natürlich einfach nach null suchen und nicht den Try/Catch verwenden.

Im Allgemeinen ist die Verwendung der Ausnahmebehandlung für Programmablauf und Logik eine schlechte Praxis.Ich persönlich halte die letztgenannte Option für eine inakzeptable Verwendung von Ausnahmen.Angesichts der Funktionen heutzutage häufig verwendeter Sprachen (z. B. Linq und Lambdas in C#) gibt es keinen Grund, nicht Ihre eigene Contains()-Methode zu schreiben.

Als letzter Gedanke: Heutzutage die meisten Sammlungen Tun Ich habe bereits eine Includes-Methode.Daher denke ich, dass dies größtenteils kein Problem darstellt.

Ausnahmen sollten außergewöhnlich sein.

Etwas wie „Die Sammlung fehlt, weil die Datenbank darunter herausgefallen ist“ ist eine Ausnahme

Etwas wie „Der Schlüssel ist nicht vorhanden“ ist ein normales Verhalten für ein Wörterbuch.

Für Ihr spezifisches Beispiel einer Winforms-Steuerelementsammlung: Controls Eigentum hat eine ContainsKey Methode, die Sie verwenden sollten.

Da ist kein ContainsValue Denn wenn man mit Wörterbüchern/Hashtabellen arbeitet, kommt man nicht umhin, die gesamte Sammlung zu durchlaufen und zu prüfen, ob etwas vorhanden ist. Davon wird man wirklich abgeraten.

Bei der Frage, WARUM Ausnahmen außergewöhnlich sein sollten, geht es um zwei Dinge

  1. Gibt an, was Ihr Code zu tun versucht.Sie möchten, dass Ihr Code so genau wie möglich mit dem übereinstimmt, was er erreichen möchte, damit er lesbar und wartbar ist.Die Ausnahmebehandlung fügt eine Menge zusätzlichen Aufwand hinzu, der diesem Zweck im Wege steht

  2. Kürze des Codes.Sie möchten, dass Ihr Code das, was er tut, auf möglichst direkte Weise ausführt, damit er lesbar und wartbar ist.Auch hier steht der durch die Ausnahmebehandlung verursachte Aufwand im Weg.

Schauen Sie sich diesen Blogbeitrag von Krzystof an: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

Ausnahmen sollten zur Kommunikation von Fehlerbedingungen verwendet werden, sie sollten jedoch nicht als Steuerlogik verwendet werden (insbesondere, wenn es weitaus einfachere Möglichkeiten gibt, eine Bedingung zu bestimmen, z. B. Enthält).

Ein Teil des Problems besteht darin, dass Ausnahmen zwar nicht teuer sind werfen sind teuer fangen und alle Ausnahmen werden irgendwann abgefangen.

Letzteres ist eine akzeptable Lösung.Obwohl ich auf jeden Fall die spezifische Ausnahme (ElementNotFound?) erkennen würde, die die Sammlung in diesem Fall auslöst.

In Bezug auf die Geschwindigkeit kommt es auf den Einzelfall an.Wenn die Wahrscheinlichkeit größer ist, dass Sie das Element finden, ist die Ausnahmelösung schneller.Wenn die Wahrscheinlichkeit eines Scheiterns höher ist, hängt dies von der Größe der Sammlung und ihrer Iterationsgeschwindigkeit ab.In jedem Fall sollten Sie zunächst anhand der normalen Nutzung messen, ob es sich tatsächlich um einen Engpass handelt, bevor Sie sich Gedanken über die Geschwindigkeit machen.Streben Sie zunächst nach Klarheit, und die letztere Lösung ist weitaus klarer als die erstere.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top