Mit unserem öffentlichen SDK möchten wir in der Regel sehr informative Nachrichten darüber geben, warum eine Ausnahme auftritt. Beispielsweise:
if (interfaceInstance == null)
{
string errMsg = string.Format(
"Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
ParameterInfo.Name,
ParameterInfo.ParameterType,
typeof(IParameter)
);
throw new InvalidOperationException(errMsg);
}
Dies führt jedoch dazu, dass der Codefluss unübersichtlich wird, da der Schwerpunkt eher auf Fehlermeldungen als auf den Aktionen des Codes liegt.
Ein Kollege hat begonnen, einige der Ausnahmen zu überarbeiten, die sich auf Folgendes auswirken:
if (interfaceInstance == null)
throw EmptyConstructor();
...
private Exception EmptyConstructor()
{
string errMsg = string.Format(
"Construction of Action Argument: {0}, via the empty constructor worked, but type: {1} could not be cast to type {2}.",
ParameterInfo.Name,
ParameterInfo.ParameterType,
typeof(IParameter)
);
return new InvalidOperationException(errMsg);
}
Dies erleichtert das Verständnis der Codelogik, fügt jedoch viele zusätzliche Methoden zur Fehlerbehandlung hinzu.
Was sind andere Möglichkeiten, um das Problem der "Clutter-Logik für Nachrichten mit langen Ausnahmen" zu vermeiden? Ich frage hauptsächlich nach idiomatischem C # /. NET, aber wie andere Sprachen damit umgehen, ist auch hilfreich.
[Bearbeiten]
Es wäre schön, auch die Vor- und Nachteile jedes Ansatzes zu haben.
quelle
Exception.Data
Eigenschaft, dem "wählerischen" Ausnahmefangen, dem Aufrufen von Code und dem Hinzufügen eines eigenen Kontexts sowie dem erfassten Aufrufstapel trägt alle Informationen bei, die weitaus weniger ausführliche Nachrichten ermöglichen sollten. SchließlichSystem.Reflection.MethodBase
sieht es vielversprechend aus, Details bereitzustellen, die an Ihre "Ausnahmekonstruktions" -Methode übergeben werden können.Exception.Data
. Der Schwerpunkt sollte auf der Erfassung der Telemetrie liegen. Refactoring hier ist in Ordnung, aber es übersieht das Problem.Antworten:
Warum nicht spezielle Ausnahmeklassen?
Dadurch werden Formatierung und Details auf die Ausnahme selbst verschoben, und die Hauptklasse bleibt übersichtlich.
quelle
Microsoft scheint (über die .NET-Quelle) manchmal Ressourcen- / Umgebungszeichenfolgen zu verwenden. Zum Beispiel
ParseDecimal
:Vorteile:
Nachteile:
quelle
Für das öffentliche SDK-Szenario würde ich dringend die Verwendung von Microsoft-Codeverträgen in Betracht ziehen, da diese informative Fehler und statische Überprüfungen enthalten und Sie auch Dokumentationen erstellen können, die in XML-Dokumente und von Sandcastle generierte Hilfedateien eingefügt werden. Es wird in allen kostenpflichtigen Versionen von Visual Studio unterstützt.
Ein zusätzlicher Vorteil besteht darin, dass Ihre Kunden, wenn sie C # verwenden, Ihre Codevertragsreferenz-Assemblys nutzen können, um potenzielle Probleme zu erkennen, noch bevor sie ihren Code ausführen.
Die vollständige Dokumentation für Codeverträge finden Sie hier .
quelle
Die Technik, die ich verwende, besteht darin , die Validierung zu kombinieren und auszulagern und insgesamt an eine Utility-Funktion zu werfen.
Der wichtigste Vorteil ist, dass er in der Geschäftslogik auf einen Einzeiler reduziert wird .
Ich wette, Sie können es nicht besser machen, wenn Sie es nicht weiter reduzieren können - um alle Argumentvalidierungen und Objektstatuswächter aus der Geschäftslogik zu entfernen und nur die betrieblichen Ausnahmebedingungen beizubehalten.
Es gibt natürlich Möglichkeiten, dies zu tun - stark typisierte Sprache, "keine ungültigen Objekte jederzeit zulässig" Design, Design by Contract usw.
Beispiel:
quelle
[Anmerkung] Ich habe dies aus der Frage in eine Antwort kopiert, falls es Kommentare dazu gibt.
Verschieben Sie jede Ausnahme in eine Methode der Klasse und berücksichtigen Sie dabei alle Argumente, die formatiert werden müssen.
Schließen Sie alle Ausnahmemethoden in die Region ein und platzieren Sie sie am Ende der Klasse.
Vorteile:
Nachteile:
quelle
#region #endregion
(wodurch sie standardmäßig vor der IDE ausgeblendet werden). Wenn sie für verschiedene Klassen gelten, sollten Sie sie einerinternal static ValidationUtility
Klasse zuordnen. Übrigens, beschweren Sie sich nie über lange Bezeichnernamen vor einem C # -Programmierer.Wenn Sie mit etwas allgemeineren Fehlern davonkommen können, können Sie eine öffentliche statische generische Cast-Funktion für Sie schreiben, die den Quelltyp ableitet:
Es sind Variationen möglich (denken Sie
SdkHelper.RequireNotNull()
), die nur die Anforderungen an die Eingaben überprüfen und werfen, wenn sie fehlschlagen. In diesem Beispiel ist die Kombination der Besetzung mit der Erstellung des Ergebnisses jedoch selbstdokumentierend und kompakt.Wenn Sie sich in .net 4.5 befinden, kann der Compiler den Namen der aktuellen Methode / Datei als Methodenparameter einfügen (siehe CallerMemberAttibute ). Aber für ein SDK können Sie wahrscheinlich nicht verlangen, dass Ihre Kunden zu 4.5 wechseln.
quelle
Was wir für Geschäftslogikfehler (nicht unbedingt Argumentationsfehler usw.) tun möchten, ist eine einzige Aufzählung, die alle möglichen Fehlertypen definiert:
Die
[Display(Name = "...")]
Attribute definieren den Schlüssel in den Ressourcendateien, der zum Übersetzen der Fehlermeldungen verwendet werden soll.Diese Datei kann auch als Ausgangspunkt verwendet werden, um alle Vorkommen zu finden, bei denen ein bestimmter Fehlertyp in Ihrem Code generiert wird.
Das Überprüfen von Geschäftsregeln kann an spezialisierte Validator-Klassen delegiert werden, die Listen mit verletzten Geschäftsregeln liefern.
Wir verwenden dann einen benutzerdefinierten Ausnahmetyp, um die verletzten Regeln zu transportieren:
Backend-Serviceaufrufe werden in generischen Fehlerbehandlungscode eingeschlossen, der die verletzte Busines-Regel in eine vom Benutzer lesbare Fehlermeldung übersetzt:
Hier
ToTranslatedString()
ist eine Erweiterungsmethodeenum
, mit der Ressourcenschlüssel aus[Display]
Attributen gelesen undResourceManager
zum Übersetzen dieser Schlüssel verwendet werden können. Der Wert für den jeweiligen Ressourcenschlüssel kann Platzhalter für enthaltenstring.Format
, die mit den angegebenen übereinstimmenMessageParameters
. Beispiel für einen Eintrag in der resx-Datei:Anwendungsbeispiel:
Mit diesem Ansatz können Sie die Generierung der Fehlermeldung von der Generierung des Fehlers entkoppeln, ohne für jeden neuen Fehlertyp eine neue Ausnahmeklasse einführen zu müssen. Nützlich, wenn unterschiedliche Frontends unterschiedliche Nachrichten anzeigen sollen, wenn die angezeigte Nachricht von der Benutzersprache und / oder Rolle usw. abhängen soll.
quelle
Ich würde Schutzklauseln wie in diesem Beispiel verwenden, damit Parameterprüfungen wiederverwendet werden können.
Darüber hinaus können Sie das Builder-Muster verwenden, sodass mehr als eine Validierung verkettet werden kann. Es wird ein bisschen wie fließende Behauptungen aussehen .
quelle