Angenommen, ich habe eine Methode:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
Ich bin darauf trainiert zu glauben, dass das Werfen von ArgumentNullException
"richtig" ist, aber ein Fehler "Objektreferenz nicht auf eine Instanz eines Objekts festgelegt" bedeutet, dass ich einen Fehler habe.
Warum?
Ich weiß, dass, wenn ich den Verweis zwischengespeichert habe someObject
und später verwende, es besser ist, bei der Übergabe auf Ungültigkeit zu prüfen und früh zu scheitern. Wenn ich es jedoch in der nächsten Zeile dereferenziere, warum sollten wir dann die Überprüfung durchführen? Es wird auf die eine oder andere Weise eine Ausnahme auslösen.
Bearbeiten :
Mir ist gerade in den Sinn gekommen ... Kommt die Angst vor dereferenzierten Null aus einer Sprache wie C ++, die nicht auf Sie prüft (dh es wird nur versucht, eine Methode am Speicherort Null + Methodenoffset auszuführen)?
Antworten:
Ich nehme an, wenn Sie die Variable sofort dereferenzieren, könnten Sie so oder so diskutieren, aber ich würde immer noch die ArgumentNullException vorziehen.
Es ist viel expliziter darüber, was los ist. Die Ausnahme enthält den Namen der Variablen, die null war, während eine NullReferenceException dies nicht tut. Insbesondere auf API-Ebene macht eine ArgumentNullException deutlich, dass ein Aufrufer den Vertrag einer Methode nicht eingehalten hat und der Fehler nicht unbedingt ein zufälliger Fehler oder ein tieferes Problem war (obwohl dies möglicherweise immer noch der Fall ist). Sie sollten früh scheitern.
Was tun spätere Betreuer außerdem mit größerer Wahrscheinlichkeit? Fügen Sie die ArgumentNullException hinzu, wenn sie vor der ersten Dereferenzierung Code hinzufügen, oder fügen Sie einfach den Code hinzu und lassen Sie später zu, dass eine NullReferenceException ausgelöst wird.
quelle
Angenommen, ich rufe
M(x)
die von Ihnen geschriebene Methode auf . Ich übergebe null. Ich erhalte eine ArgumentNullException mit dem Namen "x". Diese Ausnahme bedeutet eindeutig, dass ich einen Fehler habe; Ich hätte für x nicht null übergeben sollen.Angenommen, ich rufe eine Methode M auf, die Sie geschrieben haben. Ich übergebe null. Ich erhalte eine Null-Deref-Ausnahme. Wie zum Teufel soll ich wissen, ob ich einen Fehler habe oder ob Sie einen Fehler haben oder ob eine Bibliothek eines Drittanbieters, die Sie in meinem Namen aufgerufen haben, einen Fehler hat? Ich weiß nicht, ob ich meinen Code reparieren oder Sie anrufen muss, um Ihnen mitzuteilen, dass Sie Ihren reparieren sollen.
Beide Ausnahmen weisen auf Fehler hin. Weder sollte jemals in Produktionscode geworfen werden. Die Frage ist, welche Ausnahme der Person, die das Debuggen durchführt, mitteilt, wer den Fehler besser hat . Die Null-Deref-Ausnahme sagt Ihnen fast nichts. Argument Null Ausnahme sagt Ihnen viel. Seien Sie freundlich zu Ihren Nutzern; wirf Ausnahmen aus, die es ihnen ermöglichen, aus ihren Fehlern zu lernen.
quelle
"neither should ever be thrown in production code"
Welche anderen Arten von Code / Situationen würden sie verwenden? Sollen sie so zusammengestellt werdenDebug.Assert
? Oder meinst du sie nicht aus einer API geworfen?throw new ArgumentNullException
in Ihrem Produktionscode, und es sollte nie außerhalb eines Testfall ausgeführt . Die Ausnahme sollte eigentlich niemals geworfen werden, da der Aufrufer niemals diesen Fehler haben sollte.Der Punkt ist, Ihr Code sollte etwas Sinnvolles tun.
A
NullReferenceException
ist nicht besonders aussagekräftig, weil es über alles bedeuten könnte. Ohne nachzusehen, wo die Ausnahme ausgelöst wurde (und der Code mir möglicherweise nicht zur Verfügung steht), kann ich nicht herausfinden, was schief gelaufen ist.Ein
ArgumentNullException
ist sinnvoll, weil es mir sagt: Die Operation ist fehlgeschlagen, weil das ArgumentsomeObject
warnull
. Ich muss nicht auf Ihren Code schauen, um das herauszufinden.Das Auslösen dieser Ausnahme bedeutet auch, dass Sie explizit behandeln
null
, obwohl Ihre einzige Möglichkeit darin besteht, zu sagen, dass Sie nicht damit umgehen können. Wenn Sie jedoch den Code zum Absturz bringen, kann dies möglicherweise dazu führen, dass Sie tatsächlich in der Lage sind, mit null umzugehen , aber vergessen, den Fall umzusetzen.Ausnahmen bilden die Übermittlung von Informationen im Fehlerfall, die zur Laufzeit verarbeitet werden können, um einen Fehler zu beheben, oder zur Debug-Zeit, um besser zu verstehen, was schief gelaufen ist.
quelle
Ich glaube, es ist nur von Vorteil, dass Sie in Ausnahmefällen eine bessere Diagnose liefern können. Das heißt, Sie wissen, dass der Aufrufer der Funktion null übergibt, während Sie, wenn Sie die Dereferenzierung auslösen lassen, nicht sicher sind, ob der Aufrufer falsche Daten übergibt oder ob ein Fehler in der Funktion selbst vorliegt.
Ich würde es tun, wenn jemand anderes die Funktion aufruft, um die Verwendung zu vereinfachen (Debuggen, wenn etwas schief geht), und nicht in meinem internen Code, da die Laufzeitumgebung es trotzdem überprüft.
quelle
Sie haben einen Fehler, wenn der Code nicht wie vorgesehen funktioniert. Ein
NullReferenceException
wird vom System geworfen und diese Tatsache macht mehr aus einem Fehler als aus einer Funktion. Nicht nur, weil Sie nicht die spezifische Ausnahme definiert haben, die behandelt werden soll. Auch weil ein Entwickler, der Ihren Code liest, davon ausgehen kann, dass Sie die aufgetretene Situation nicht berücksichtigt haben.Aus ähnlichen Gründen
ApplicationException
gehört das zu Ihrer Anwendung und ein allgemeinesException
zum Betriebssystem. Der Typ der ausgelösten Ausnahme gibt an, woher die Ausnahme stammt.Wenn Sie für null gerechnet haben, ist es besser, wenn Sie es ordnungsgemäß handhaben, indem Sie eine bestimmte Ausnahme auslösen, oder wenn es sich um eine akzeptable Eingabe handelt, dann werfen Sie keine Ausnahme, weil sie teuer ist. Behandeln Sie das Problem, indem Sie Vorgänge mit guten Standardeinstellungen oder ohne Vorgänge ausführen (z. B. kein Update erforderlich).
Vernachlässigen Sie nicht die Fähigkeit des Anrufers, mit der Situation umzugehen. Indem sie ihnen eine spezifischere Ausnahme geben, können
ArgumentException
sie ihren Versuch / Fang so konstruieren, dass er diesen Fehler behandelt, bevor er allgemeiner wird. DiesNullReferenceException
kann durch eine Reihe anderer Gründe verursacht werden, die an anderer Stelle auftreten, z. B. ein Fehler beim Initialisieren einer Konstruktorvariablen.quelle
Die Prüfung macht es klarer, was los ist, aber das eigentliche Problem ergibt sich aus folgendem (erfundenen) Code:
Hier handelt es sich um eine Aufrufkette, bei der ohne Nullprüfung nur der Fehler in someOtherObject angezeigt wird und nicht, wo das Problem zum ersten Mal aufgetreten ist. Dies bedeutet, dass sofort Zeit für das Debuggen oder Interpretieren von Aufrufstapeln verschwendet wird.
Im Allgemeinen ist es besser, mit dieser Art von Dingen im Einklang zu sein und immer den Null-Check hinzuzufügen - was es einfacher macht, zu erkennen, wenn einer fehlt, anstatt von Fall zu Fall auszuwählen (vorausgesetzt, Sie wissen, dass Null ist) immer ungültig).
quelle
Ein weiterer zu berücksichtigender Grund ist, was zu tun ist, wenn eine Verarbeitung erfolgt, bevor das Nullobjekt verwendet wird?
Angenommen, Sie haben eine Datendatei mit zugeordneten Firmen-IDs (Cid) und Personen-IDs (Pid). Es wird als Stream von Zahlenpaaren gespeichert:
Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid
Und diese VB-Funktion schreibt die Datensätze in die Datei:
Wenn
Person
es sich um eine Nullreferenz handelt, löst die ZeileFile.Write(Person.ID)
eine Ausnahme aus. Die Software kann die Ausnahme abfangen und wiederherstellen, dies hinterlässt jedoch ein Problem. Eine Firmen-ID wurde ohne zugehörige Personen-ID geschrieben. Wenn Sie diese Funktion das nächste Mal aufrufen, geben Sie eine Firmen-ID ein, unter der die vorherige Personen-ID erwartet wurde. Jeder nachfolgende Datensatz enthält eine Personen-ID, gefolgt von einer Firmen-ID. Dies ist der falsche Weg.Cid Pid Cid Pid Cid Pid Cid Pid Cid
Indem Sie die Parameter zu Beginn der Funktion validieren, verhindern Sie, dass eine Verarbeitung stattfindet, bei der ein Fehlschlagen der Methode garantiert ist.
quelle