Was ist die beste Vorgehensweise für die Validierung von Konstruktorparametern?
Angenommen, ein einfaches Stück C #:
public class MyClass
{
public MyClass(string text)
{
if (String.IsNullOrEmpty(text))
throw new ArgumentException("Text cannot be empty");
// continue with normal construction
}
}
Wäre es akzeptabel, eine Ausnahme auszulösen?
Die Alternative, auf die ich gestoßen bin, war die Vorvalidierung vor der Instanziierung:
public class CallingClass
{
public MyClass MakeMyClass(string text)
{
if (String.IsNullOrEmpty(text))
{
MessageBox.Show("Text cannot be empty");
return null;
}
else
{
return new MyClass(text);
}
}
}
c#
validation
constructors
MPelletier
quelle
quelle
Antworten:
Ich neige dazu, alle meine Validierungen im Konstruktor durchzuführen. Dies ist ein Muss, da ich fast immer unveränderliche Objekte erstelle. Für Ihren speziellen Fall halte ich das für akzeptabel.
Wenn Sie .NET 4 verwenden, können Sie dies tun. Dies hängt natürlich davon ab, ob Sie eine Zeichenfolge, die nur Leerzeichen enthält, für ungültig halten.
quelle
Init
, um eine Art Fehlercode zu erhalten, wenn eine Ausnahme genauso gut funktioniert, und Ihr Objekt dazu zwingen, immer einen gültigen Status beizubehalten?ArgumentNullException
andere, die auf leer prüft und eine auslöstArgumentException
. Ermöglicht es dem Anrufer, wählerischer zu sein, wenn er die Ausnahmebehandlung durchführen möchte.Viele Leute sagen, dass Konstrukteure keine Ausnahmen werfen sollten. KyleG auf dieser Seite macht zum Beispiel genau das. Ehrlich gesagt, ich kann mir keinen Grund vorstellen, warum nicht.
In C ++ ist es eine schlechte Idee, eine Ausnahme von einem Konstruktor auszulösen, da Ihnen dadurch Speicher zugewiesen wird, der ein nicht initialisiertes Objekt enthält, auf das Sie keinen Bezug haben (dh es ist ein klassischer Speicherverlust). Das Stigma kommt wahrscheinlich von dort - eine Reihe von C ++ - Entwicklern der alten Schule haben ihr C # -Lernen zur Hälfte abgebrochen und nur das angewendet, was sie von C ++ kannten. Im Gegensatz dazu hat Apple in Objective-C den Zuweisungsschritt vom Initialisierungsschritt getrennt, sodass Konstruktoren in dieser Sprache Ausnahmen auslösen können .
C # kann keinen Speicher von einem erfolglosen Aufruf an einen Konstruktor verlieren. Sogar einige Klassen im .NET Framework lösen Ausnahmen in ihren Konstruktoren aus.
quelle
Eine Ausnahme auslösen, wenn die Klasse in Bezug auf ihre semantische Verwendung nicht in einen konsistenten Zustand versetzt werden kann. Ansonsten nicht. NIEMALS zulassen, dass ein Objekt in einem inkonsistenten Zustand existiert. Dazu gehört, dass keine vollständigen Konstruktoren bereitgestellt werden (z. B. ein leerer Konstruktor + initialize (), bevor Ihr Objekt tatsächlich vollständig erstellt wurde) ... SAGEN SIE NUR NEIN!
Zur Not macht es jeder. Ich habe es neulich für ein sehr eng genutztes Objekt in einem engen Rahmen gemacht. Eines Tages werde ich oder jemand anderes wahrscheinlich den Preis für diesen Zettel in der Praxis bezahlen.
Ich sollte beachten, dass mit "Konstruktor" das gemeint ist, was der Client aufruft, um das Objekt zu erstellen. Das könnte genauso gut etwas anderes als ein tatsächliches Konstrukt sein, das den Namen "Konstruktor" trägt. Zum Beispiel würde so etwas in C ++ das Prinzip IMNSHO nicht verletzen:
Da der einzige Weg, ein Objekt zu erstellen, darin
funky_object
besteht,build_funky
das Prinzip aufzurufen, niemals zuzulassen, dass ein ungültiges Objekt existiert, bleibt es intakt, obwohl der eigentliche "Konstruktor" den Job nicht beendet.Das ist eine Menge zusätzlicher Arbeit für fragwürdigen Gewinn (vielleicht sogar Verlust). Ich würde immer noch die Ausnahmeroute bevorzugen.
quelle
In diesem Fall würde ich die Factory-Methode verwenden. Grundsätzlich sollten Sie festlegen, dass Ihre Klasse nur private Konstruktoren und eine Factory-Methode hat, die eine Instanz Ihres Objekts zurückgibt. Wenn die Anfangsparameter ungültig sind, geben Sie einfach null zurück und lassen Sie den aufrufenden Code entscheiden, was zu tun ist.
Werfen Sie niemals Ausnahmen, es sei denn, die Bedingungen sind außergewöhnlich. Ich denke, dass in diesem Fall ein leerer Wert leicht übergeben werden kann. Wenn dies der Fall ist, werden durch die Verwendung dieses Musters Ausnahmen und die damit verbundenen Leistungseinbußen vermieden, während der MyClass-Status gültig bleibt.
quelle
Man könnte diese Richtlinien vernünftigerweise in Frage stellen und sagen: "Aber ich halte mich nicht an diese Regeln und mein Code funktioniert einwandfrei!" Darauf würde ich antworten: "Nun, das mag wahr sein, bis es nicht mehr so ist."
quelle
Es kommt darauf an, was MyClass macht. Wenn MyClass tatsächlich eine Datenrepository-Klasse und Parametertext eine Verbindungszeichenfolge ist, empfiehlt es sich, eine ArgumentException auszulösen. Wenn MyClass jedoch (zum Beispiel) die StringBuilder-Klasse ist, können Sie sie einfach leer lassen.
Es kommt also darauf an, wie wichtig der Parametertext für die Methode ist. Ist das Objekt mit einem Null- oder Leerwert sinnvoll?
quelle
Ich bevorzuge es, einen Standard festzulegen, aber ich weiß, dass Java die "Apache Commons" -Bibliothek hat, die so etwas tut, und ich denke, das ist auch eine ziemlich gute Idee. Ich sehe kein Problem beim Auslösen einer Ausnahme, wenn der ungültige Wert das Objekt in einen unbrauchbaren Zustand versetzen würde. Eine Saite ist kein gutes Beispiel, aber was ist, wenn es sich um die DI eines armen Mannes handelt? Sie könnten nicht arbeiten, wenn ein Wert von
null
anstelle einerICustomerRepository
Schnittstelle übergeben würde. In einer Situation wie dieser ist das Auslösen einer Ausnahme die richtige Vorgehensweise, würde ich sagen.quelle
Als ich in c ++ gearbeitet habe, habe ich keine Ausnahme im Konstruktor ausgelöst, da dies zu einem Speicherverlust führen kann. Ich hatte es auf die harte Tour gelernt. Jeder, der mit c ++ arbeitet, weiß, wie schwierig und problematisch ein Speicherverlust sein kann.
Wenn Sie sich jedoch in c # / Java befinden, haben Sie dieses Problem nicht, da der Garbage Collector den Speicher sammelt. Wenn Sie C # verwenden, ist es meiner Meinung nach vorzuziehen, die Eigenschaft zu verwenden, um sicherzustellen, dass Dateneinschränkungen garantiert sind.
quelle
Das Auslösen einer Ausnahme ist für die Benutzer Ihrer Klasse eine echte Belastung. Wenn die Validierung ein Problem darstellt, wird das Erstellen des Objekts im Allgemeinen in zwei Schritten ausgeführt.
1 - Erstellen Sie die Instanz
2 - Rufen Sie eine Initialize-Methode mit einem Rückgabeergebnis auf.
ODER
Rufen Sie eine Methode / Funktion auf, die die Klasse erstellt, die die Validierung durchführen kann.
ODER
Haben Sie eine isValid-Flagge, die ich nicht besonders mag, aber einige Leute tun.
quelle
isValid = false
. Nach dem Erstellen einer Klasse testen zu müssen, ob sie gültig ist, ist ein schreckliches, sehr fehleranfälliges Design. Und, sagten Sie, haben Sie einen Konstruktor und rufen Sie dann initialize auf ... Was, wenn ich nicht initialize aufrufe? Was dann? Und was ist, wenn Initialize fehlerhafte Daten erhält? Kann ich jetzt eine Ausnahme auslösen? Ihre Klasse sollte nicht schwer zu bedienen sein.