Ich bin ein großer Fan von Zusicherungen, Verträgen oder anderen Arten von Schecks, die in der von mir verwendeten Sprache verfügbar sind. Eine Sache, die mich ein bisschen stört, ist, dass ich nicht sicher bin, wie es üblich ist, mit doppelten Prüfungen umzugehen.
Beispielsituation: Ich schreibe zuerst die folgende Funktion
void DoSomething( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
//code using obj
}
dann schreibe ich ein paar Stunden später eine andere Funktion, die die erste aufruft. Da alles noch frisch im Speicher ist, entscheide ich mich, den Vertrag nicht zu duplizieren, da ich weiß, dass DoSomething
bereits nach einem Nullobjekt gesucht wird:
void DoSomethingElse( object obj )
{
//no Requires here: DoSomething will do that already
DoSomething( obj );
//code using obj
}
Das offensichtliche Problem: DoSomethingElse
hängt jetzt davon ab DoSomething
, ob obj nicht null ist. DoSomething
Sollte sich also jemals entscheiden, nicht mehr zu prüfen, oder wenn ich mich entscheide, eine andere Funktion zu verwenden, wird obj möglicherweise nicht mehr geprüft. Was mich schließlich dazu bringt, diese Implementierung zu schreiben:
void DoSomethingElse( object obj )
{
Contract.Requires<ArgumentNullException>( obj != null );
DoSomething( obj );
//code using obj
}
Immer sicher, keine Sorge, außer dass, wenn die Situation wächst, dasselbe Objekt möglicherweise mehrmals überprüft wird und es sich um eine Form der Vervielfältigung handelt, und wir alle wissen, dass dies nicht so gut ist.
Was ist die gängigste Praxis für solche Situationen?
quelle
ArgumentBullException
? Das ist neu :)Antworten:
Persönlich würde ich in jeder Funktion, die fehlschlägt, wenn sie eine Null erhält, nach Null suchen, und nicht in einer Funktion, die dies nicht tut.
Wenn also in Ihrem obigen Beispiel doSomethingElse () obj nicht dereferenzieren muss, würde ich obj dort nicht auf null prüfen.
Wenn DoSomething () das Objekt dereferenziert, sollte es auf null prüfen.
Wenn beide Funktionen es dereferenzieren, sollten sie beide prüfen. Wenn DoSomethingElse dereferences obj ist, sollte es auf null prüfen, aber DoSomething sollte auch weiterhin auf null prüfen, da es möglicherweise von einem anderen Pfad aufgerufen wird.
Auf diese Weise können Sie den Code ziemlich sauber lassen und trotzdem sicherstellen, dass die Überprüfungen an der richtigen Stelle sind.
quelle
DoSomething()
, sodass die Vorbedingung nicht mehr erforderlich ist (in diesem speziellen Fall unwahrscheinlich, kann aber in einer anderen Situation auftreten), und entfernen Sie die Voraussetzungsprüfung. Jetzt ist eine scheinbar völlig unabhängige Methode wegen der fehlenden Voraussetzung gebrochen. Ich werde ein wenig Code duplizieren, um Klarheit über solche seltsamen Fehler zu schaffen, aus dem Wunsch heraus, jeden Tag ein paar Codezeilen zu speichern.Groß! Ich sehe, Sie haben von Code Contracts for .NET erfahren. Codeverträge gehen weit über Ihre durchschnittlichen Behauptungen hinaus, wofür der statische Prüfer das beste Beispiel ist. Dies steht Ihnen möglicherweise nicht zur Verfügung, wenn Sie Visual Studio Premium oder höher nicht installiert haben. Es ist jedoch wichtig, die Absichten dahinter zu verstehen, wenn Sie Codeverträge verwenden möchten.
Wenn Sie einen Vertrag auf eine Funktion anwenden, handelt es sich buchstäblich um einen Vertrag . Diese Funktion garantiert ein vertragsgemäßes Verhalten und wird garantiert nur gemäß der vertraglichen Definition verwendet.
In Ihrem Beispiel entspricht die
DoSomethingElse()
Funktion nicht dem von angegebenen WertDoSomething()
, da null übergeben werden kann und der statische Prüfer dieses Problem anzeigt. Sie können dies beheben, indem Sie denselben Vertrag hinzufügenDoSomethingElse()
.Dies bedeutet, dass es zu einer Duplizierung kommt. Diese Duplizierung ist jedoch erforderlich, wenn Sie die Funktionalität für zwei Funktionen verfügbar machen. Obwohl diese Funktionen privat sind, können sie auch von verschiedenen Stellen in Ihrer Klasse aufgerufen werden. Die einzige Möglichkeit, um sicherzustellen, dass das Argument bei einem bestimmten Aufruf niemals null ist, besteht darin, die Verträge zu duplizieren.
Dies sollte Sie überdenken lassen, warum Sie das Verhalten überhaupt in zwei Funktionen aufgeteilt haben. Ich war immer der Meinung ( entgegen der landläufigen Meinung ), dass man Funktionen, die nur von einem Ort aus aufgerufen werden, nicht aufteilen sollte . Durch das Aufdecken der Kapselung durch Anwenden der Verträge wird dies noch deutlicher. Es scheint, ich habe eine zusätzliche Argumentation für meine Sache gefunden! Vielen Dank! :) :)
quelle