Wir haben diesen Code, der vereinfacht so aussieht:
public class Room
{
public Client Client { get; set; }
public long ClientId
{
get
{
return Client == null ? 0 : Client.Id;
}
}
}
public class Client
{
public long Id { get; set; }
}
Jetzt haben wir drei Standpunkte.
1) Dies ist ein guter Code, da die Client
Eigenschaft immer festgelegt werden sollte (dh nicht null), damit Client == null
sie niemals auftritt und der ID-Wert 0
ohnehin eine falsche ID angibt (dies ist die Meinung des Verfassers des Codes ;-))
2) Sie können nicht auf den Anrufer verlassen , um zu wissen , dass 0
ein falscher Wert für Id
und wenn die Client
Eigenschaft immer eingestellt werden sollte , sollten Sie eine werfen exception
in der , get
wenn die Client
Eigenschaft null sein geschieht
3) Wenn die Client
Eigenschaft immer festgelegt werden soll, kehren Sie einfach zurück Client.Id
und lassen den Code eine NullRef
Ausnahme Client
auslösen, wenn die Eigenschaft zufällig null ist.
Welche davon ist am richtigsten? Oder gibt es eine vierte Möglichkeit?
quelle
Antworten:
Es riecht so, als sollten Sie die Anzahl der Zustände begrenzen, in denen sich Ihre
Room
Klasse befinden kann.Die Tatsache, dass Sie fragen, was zu tun ist, wenn
Client
null ist, ist ein Hinweis darauf, dassRoom
der Statusraum zu groß ist.Um die Dinge einfach zu halten, würde ich nicht zulassen, dass die
Client
Eigenschaft einerRoom
Instanz jemals null ist. Das bedeutet, dass der darin enthaltene CodeRoom
sicher annehmen kann, dass derClient
niemals null ist.Wenn aus irgendeinem Grunde in der Zukunft
Client
wirdnull
den Drang widerstehen , diesen Zustand zu unterstützen. Dies erhöht Ihre Wartungskomplexität.Lassen Sie stattdessen zu, dass der Code fehlschlägt und schnell fehlschlägt. Dies ist schließlich kein unterstützter Zustand. Wenn sich die Anwendung in diesen Zustand versetzt, haben Sie bereits eine Linie ohne Rückgabe überschritten. Das einzig Vernünftige ist dann, die Anwendung zu schließen.
Dies kann (ganz natürlich) als Ergebnis einer nicht behandelten Nullreferenzausnahme geschehen.
quelle
null
Wert durch einenNullObject
(oder besserNullClient
) ersetzen, der dann weiterhin mit Ihrem vorhandenen Code funktioniert, und bei Bedarf das Verhalten für einen nicht vorhandenen Client definieren. Die Frage ist, ob der Fall "kein Client" Teil der Geschäftslogik ist oder nicht.Nur ein paar Überlegungen:
a) Warum gibt es einen Getter speziell für die ClientId, wenn es bereits einen öffentlichen Getter für die Client-Instanz selbst gibt? Ich verstehe nicht, warum die Information, dass es sich bei der ClientId um eine handelt
long
, in die Signatur von Room in Stein gemeißelt werden muss.b) In Bezug auf die zweite Meinung könnten Sie eine Konstante einführen
Invalid_Client_Id
.c) In Bezug auf Meinung eins und drei (und als mein Hauptpunkt): Ein Raum muss immer einen Kunden haben? Vielleicht ist es nur Semantik, aber das klingt nicht richtig. Vielleicht wäre es angemessener, vollständig getrennte Klassen für Raum und Kunde und eine andere Klasse zu haben, die sie miteinander verbindet. Vielleicht Termin, Reservierung, Belegung? (Dies hängt davon ab, was Sie tatsächlich tun.) Und für diese Klasse können Sie die Einschränkungen "muss einen Raum haben" und "muss einen Kunden haben" durchsetzen.
quelle
Ich bin mit allen drei Meinungen nicht einverstanden. Wenn
Client
es niemals sein kannnull
, dann mach es nicht einmal möglichnull
!Client
im Konstruktor festArgumentNullException
in den Konstruktor.Ihr Code wäre also ungefähr so:
Dies hat nichts mit Ihrem Code zu tun, aber Sie sollten wahrscheinlich eine unveränderliche Version von verwenden,
Room
indem Sie machentheClient
readonly
und dann, wenn sich der Client ändert, einen neuen Raum erstellen . Dies verbessert die Thread-Sicherheit Ihres Codes zusätzlich zur Nullsicherheit der anderen Aspekte meiner Antwort. Siehe diese Diskussion über veränderlich gegen unveränderlichquelle
ArgumentNullException
s sein?NullReferenceException
. Er wird von der .NET-VM ausgelöst, "wenn versucht wird, eine Nullobjektreferenz zu dereferenzieren" . Sie sollten ein auslösenArgumentNullException
, das ausgelöst wird, "wenn eine Nullreferenz (Nothing in Visual Basic) an eine Methode übergeben wird, die sie nicht als gültiges Argument akzeptiert."readonly
). Ich hätte gerade bearbeitet, aber ich hatte das Gefühl, ein Kommentar würde dazu dienen, besser zu erklären, warum (für zukünftige Leser). Wenn Sie diese Antwort nicht bereits gegeben hätten, würde ich genau die gleiche Lösung geben. Unveränderlichkeit ist auch eine gute Idee, aber es ist nicht immer so einfach, wie man hofft.Die zweite und dritte Option sollten vermieden werden - der Getter sollte den Anrufer nicht schlagen, mit einer Ausnahme, über die er keine Kontrolle hat.
Sie sollten entscheiden, ob der Client jemals null sein kann. In diesem Fall sollten Sie einem Aufrufer die Möglichkeit geben, vor dem Zugriff zu überprüfen, ob er null ist (z. B. die Eigenschaft bool ClientIsNull).
Wenn Sie entscheiden, dass der Client niemals null sein kann, machen Sie ihn zu einem erforderlichen Parameter für den Konstruktor und lösen Sie dort die Ausnahme aus, wenn eine Null übergeben wird.
Schließlich enthält die erste Option auch einen Codegeruch. Sie sollten den Client mit seiner eigenen ID-Eigenschaft befassen lassen. Es scheint übertrieben, einen Getter in einer Containerklasse zu codieren, die einfach einen Getter für die enthaltene Klasse aufruft. Stellen Sie den Client einfach als Eigenschaft bereit (andernfalls duplizieren Sie am Ende alles, was ein Client bereits anbietet ).
Wenn der Client null sein kann, geben Sie dem Anrufer zumindest die Verantwortung:
quelle
Wenn eine Null-Client-Eigenschaft unterstützt wird, sollten Sie ein NullObject verwenden .
Aber höchstwahrscheinlich ist dies ein außergewöhnlicher Zustand, daher sollten Sie es unmöglich (oder einfach nicht sehr praktisch) machen, eine Null zu erhalten
Client
:Wenn es jedoch nicht unterstützt wird, verschwenden Sie keine Zeit mit halbgebackenen Lösungen. In diesem Fall:
Sie haben die NullReferenceException hier "gelöst", aber zu einem hohen Preis! Dies würde alle Anrufer von zwingen
Room.ClientId
, nach 0 zu suchen:Seltsame Fehler können auftreten, wenn andere Entwickler (oder Sie selbst!) Vergessen, den Rückgabewert "ErrorCode" zu einem bestimmten Zeitpunkt zu überprüfen.
Gehen Sie auf Nummer sicher und scheitern Sie schnell. Lassen Sie die NullReferenceException ausgelöst werden und fangen Sie sie "anmutig" irgendwo höher im Aufrufstapel ab, anstatt dem Aufrufer zu erlauben, die Dinge noch mehr durcheinander zu bringen ...
In einem anderen Sinne , wenn Sie so sehr daran interessiert sind, das
Client
und auch dasClientId
Denken über TellDontAsk aufzudecken . Wenn Sie zu viel von Objekten verlangen, anstatt ihnen zu sagen, was Sie erreichen möchten, beenden Sie möglicherweise die Kopplung aller Elemente, wodurch Änderungen später schwieriger werden.quelle
NotSupportedException
wird missbraucht, Sie sollten stattdessen eineArgumentNullException
verwenden.Ab c # 6.0, das jetzt veröffentlicht wird, sollten Sie dies einfach tun:
Das Erhöhen einer Eigenschaft von
Client
toRoom
ist ein offensichtlicher Verstoß gegen die Kapselung und das DRY-Prinzip.Wenn Sie auf das
Id
von aClient
von a zugreifenRoom
müssen, können Sie Folgendes tun:Beachten Sie die Verwendung des Null Conditional Operator ,
clientId
wird eine seinlong?
, wennClient
istnull
,clientId
wird seinnull
, sonstclientId
wird der Wert habenClient.Id
.quelle
clientid
ist dann eine nullable lange?var clientId = room.Client.Id
wird sowohl sicher als auchlong
.