Ich habe den Ratschlag, den ich in anderen beantworteten Fragen gesehen habe, über das Auslösen von Ausnahmen gelesen, aber jetzt haben meine APIs neues Rauschen. Anstatt Methoden aufzurufen, die in try / catch-Blöcke eingeschlossen sind (ärgerliche Ausnahmen), habe ich Parameter für Argumente mit einer Sammlung von Fehlern, die während der Verarbeitung aufgetreten sein könnten. Ich verstehe, warum alles in einen Versuch / Fang zu wickeln ist eine schlechte Möglichkeit, den Fluss einer App zu steuern, aber ich sehe selten Code, der diese Idee widerspiegelt.
Deshalb scheint mir das Ganze so seltsam zu sein. Es ist eine Praxis, die angeblich der richtige Weg ist, um zu programmieren, aber ich sehe es nirgends. Hinzu kommt, dass ich nicht verstehe, wie man sich mit Client-Code verhält, wenn "schlechtes" Verhalten aufgetreten ist.
Hier ist ein Ausschnitt aus Code, mit dem ich hantiere, der sich mit dem Speichern von Bildern beschäftigt, die von Benutzern einer Web-App hochgeladen werden. Schwitzen Sie nicht die Details (es ist hässlich), sehen Sie einfach, wie ich diese Ausgabeparameter zu allem hinzugefügt habe, um Fehlermeldungen zu erhalten.
%Vor%Ist das die richtige Idee? Ich kann vernünftigerweise erwarten, dass eine von einem Benutzer gesendete Datei in irgendeiner Weise ungültig ist, daher sollte ich keine Ausnahme auslösen, aber ich würde gerne wissen, was mit der Datei nicht stimmt, damit ich Fehlermeldungen erstelle. Ebenso möchte jeder Client, der diese Speichermethode nun verwendet, auch herausfinden, was mit der allgemeinen Bildspeicherung nicht in Ordnung war.
Ich hatte andere Ideen, ein Statusobjekt zurückzugeben, das ein Ergebnis und zusätzliche Fehlermeldungen enthielt, aber das fühlt sich komisch an. Ich weiß, dass es überall schwierig sein wird, Parameter überall zu verwalten / umgestalten / etc.
Ich würde gerne eine Anleitung dazu bekommen!
BEARBEITEN: Ich denke, dass das von Benutzern eingereichte Datei-Snippet dazu führen kann, dass Benutzer über Ausnahmen nachdenken, die durch das Laden ungültiger Bilder und anderer "harter" Fehler erzeugt werden. Ich denke, dieses Code-Snippet ist ein besseres Beispiel dafür, wo ich denke, dass die Idee, eine Ausnahme zu werfen, entmutigt wird.
Hiermit speichere ich nur ein neues Benutzerkonto. Ich mache eine Statusüberprüfung für das Benutzerkonto und dann stoße ich in den persistenten Speicher, um herauszufinden, ob der Benutzername genommen wurde.
%Vor%Sollte ich irgendeine Art von Validierungsausnahme werfen? Oder soll ich Fehlermeldungen zurückgeben?
Definitionsgemäß bedeutet "Ausnahme" einen außergewöhnlichen Umstand, aus dem eine Routine nicht wiederhergestellt werden kann. In dem von Ihnen bereitgestellten Beispiel sieht das so aus, als wäre das Bild ungültig / beschädigt / unlesbar / etc. Das sollte geworfen und bis zur obersten Ebene durchgebrannt werden, und dort entscheidet, was mit der Ausnahme geschehen soll. Die Ausnahme selbst enthält die vollständigsten Informationen darüber, was schief gelaufen ist und in den oberen Ebenen verfügbar sein muss.
Wenn Leute sagen, dass Sie keine Ausnahmen zum Steuern des Programmablaufs verwenden sollten, ist dies beispielsweise so: Wenn ein Benutzer versucht, ein Konto zu erstellen, aber das Konto bereits existiert, sollten Sie keine AccountExistsException werfen und dann höher abfangen in der Anwendung in der Lage sein, diese Rückmeldung an den Benutzer zu liefern, da der bereits vorhandene Account kein Ausnahmefall ist. Sie sollten diese Situation erwarten und als Teil Ihres normalen Programmablaufs behandeln. Wenn Sie keine Verbindung zur Datenbank herstellen können, ist das ein Ausnahmefall.
Ein Teil des Problems mit Ihrem Benutzerregistrierungsbeispiel besteht darin, dass Sie versuchen, zu viel in eine einzelne Routine zu kapseln. Wenn Ihre Methode versucht, mehr als eine Sache zu tun, dann müssen Sie den Zustand mehrerer Dinge verfolgen (daher werden die Dinge hässlich, wie Listen von Fehlermeldungen). In diesem Fall können Sie stattdessen Folgendes tun:
%Vor%Offensichtlich ist dies ein vereinfachtes Beispiel, aber ich hoffe, der Punkt ist klar: Ihre Routinen sollten nur versuchen, eine Sache zu tun, und sollten einen begrenzten / vorhersehbaren Umfang der Operation haben. Das macht es einfacher, den Programmablauf anzuhalten und umzuleiten, um mit verschiedenen Situationen fertig zu werden.
Trotz der Leistungsbedenken glaube ich, dass es sauberer ist, Ausnahmen aus einer Methode zu entfernen. Wenn es Ausnahmen gibt, die innerhalb Ihrer Methode behandelt werden können, sollten Sie sie entsprechend behandeln, aber lassen Sie sie ansonsten aufblühen.
Das Zurückgeben von Fehlern in out-Parametern oder das Zurückgeben von Statuscodes fühlt sich ein wenig klobig an. Manchmal versuche ich mir angesichts dieser Situation vorzustellen, wie das .NET-Framework mit den Fehlern umgehen würde. Ich glaube nicht, dass es viele .NET-Framework-Methoden gibt, die Fehler in out-Parametern zurückgeben oder Statuscodes zurückgeben.
Ich denke, das ist der falsche Ansatz. Ja, es ist sehr wahrscheinlich, dass Sie gelegentlich ungültige Bilder erhalten. Aber das ist immer noch das Ausnahme-Szenario. Meiner Meinung nach sind Ausnahmen die richtige Wahl.
In Situationen, wie Sie sie haben, gebe ich normalerweise eine benutzerdefinierte Ausnahme an den Anrufer. Ich habe ein bisschen eine andere Sichtweise auf Ausnahmen als andere: Wenn die Methode nicht das tun konnte, was sie beabsichtigt (dh wie der Methodenname sagt: Erstelle ein Benutzerkonto), dann sollte sie eine Ausnahme auslösen Ich: Nicht das zu tun, was man tun soll, ist außergewöhnlich.
Für das von Ihnen gepostete Beispiel hätte ich etwas wie:
%Vor%Der Vorteil, zumindest für mich, ist, dass meine Benutzeroberfläche dumm ist. Ich versuche einfach / jede Funktion und Messagebox die Exception-Nachricht wie:
%Vor%Dies ist ähnlich wie bei vielen System.IO-Methoden ( Ссылка ) ein Beispiel.
Wenn es zu einem Leistungsproblem kommt, werde ich später etwas anderes umgestalten, aber ich musste aufgrund von Ausnahmen nie die Leistung aus einer Business-App herausholen.
Ich würde auch Ausnahmen zulassen, aber basierend auf Ihrem Thread suchen Sie nach einer Alternative. Warum sollten Sie keine Status- oder Fehlerinformationen in Ihr PictureData-Objekt aufnehmen? Sie können dann einfach das Objekt mit den Fehlern zurückgeben und das andere Zeug leer lassen. Nur ein Vorschlag, aber Sie tun ziemlich genau, welche Ausnahmen gemacht wurden, um zu lösen:)
Zunächst sollten Ausnahmen niemals als Kontrollflussmechanismus verwendet werden. Ausnahmen sind ein Fehlerausbreitungs- und -verarbeitungsmechanismus, sollten jedoch niemals zur Steuerung des Programmablaufs verwendet werden. Kontrollfluss ist die Domäne von bedingten Anweisungen und Schleifen. Das ist oft ein kritisches Missverständnis, das viele Programmierer machen, und führt normalerweise zu solchen Albträumen, wenn sie versuchen, mit Ausnahmen umzugehen.
In einer Sprache wie C #, die eine strukturierte Ausnahmebehandlung bietet, besteht die Idee darin, "außergewöhnliche" Fälle in Ihrem Code zu identifizieren, zu verbreiten und schließlich zu behandeln. Die Handhabung wird im Allgemeinen der höchsten Ebene Ihrer Anwendung überlassen (dh einem Windows-Client mit einer Benutzeroberfläche und Fehlerdialogen, einer Website mit Fehlerseiten, einer Protokollierungseinrichtung in der Nachrichtenschleife eines Hintergrunddienstes usw.). Im Gegensatz zu Java, das verwendet Ausnahmebehandlung überprüft, C # erfordert nicht, dass Sie jede einzelne Ausnahme speziell behandeln, die Ihre Methoden möglicherweise durchlaufen. Im Gegenteil, der Versuch, dies zu tun, würde zweifellos zu einigen schwerwiegenden Leistungsengpässen führen, da das Fangen, Behandeln und möglicherweise erneutes Auslöschen kostspieliger Geschäfte ist.
Die allgemeine Idee mit Ausnahmen in C # ist, dass wenn sie passieren ... und ich betone if , weil sie Ausnahmen genannt werden, aufgrund der Tatsache, dass während des normalen Betriebs , Sie sollten keine außergewöhnlichen Bedingungen kennen, ... wenn sie auftreten, dann stehen Ihnen die Tools zur sicheren und sauberen Wiederherstellung und Präsentation des Benutzers zur Verfügung (wenn es gibt einen) mit einer Benachrichtigung über den Anwendungsfehler und mögliche Auflösungsoptionen.
Die meiste Zeit wird eine gut geschriebene C # -Anwendung nicht so viele try / catch-Blöcke in der Kerngeschäftslogik haben und wird viel mehr versuchen / endlich, oder noch besser, Blöcke verwenden. Für den meisten Code besteht die Sorge als Reaktion auf eine Ausnahme darin, sich durch das Freigeben von Ressourcen, Sperren usw. gut zu erholen und das Fortsetzen der Ausnahme zu erlauben. In Ihrem Code höherer Ebene, normalerweise in der äußeren Nachrichtenverarbeitungsschleife einer Anwendung oder in der Standard-Ereignisbehandlungsroutine für Systeme wie ASP.NET, führen Sie Ihre strukturierte Behandlung mit einem try / catch aus, möglicherweise mit mehreren catch-Klauseln, um mit spezifischen Fehlern umzugehen, die eine eindeutige Behandlung erfordern.
Wenn Sie Exceptions ordnungsgemäß verarbeiten und Code erstellen, der Exceptions in geeigneter Weise verwendet, sollten Sie sich nicht um viele try / catch / finally-Blöcke, Rückgabecodes oder verworrene Methodensignaturen mit vielen Referenzen kümmern Parameter. Sie sollten den Code eher wie folgt sehen:
%Vor%Wenn Sie dem obigen Paradigma folgen, haben Sie nicht viel unordentlichen Code, aber Sie sind immer noch vor Ausnahmen geschützt, die sonst Ihren normalen Programmablauf unterbrechen und bis zu Ihrer höheren Ausnahmebehandlung übergehen Code.
BEARBEITEN:
Ein guter Artikel, der die beabsichtigte Verwendung von Ausnahmen behandelt und warum Ausnahmen in C # nicht geprüft werden, ist das folgende Interview mit Anders Heijlsberg, dem Chefarchitekten der C # -Sprache:
EDIT 2:
Um ein besseres Beispiel zu geben, das mit dem von Ihnen geposteten Code zusammenarbeitet, wird das Folgende vielleicht nützlicher sein. Ich schätze einige der Namen, und Dinge zu tun, eine der Möglichkeiten, auf die ich Dienstleistungen umgesetzt habe ... also vergeben Sie jede Lizenz, die ich nehme:
%Vor%