Sind nullbare Typen magischen Zahlen vorzuziehen?

22

Ich habe in letzter Zeit ein wenig mit einem Kollegen diskutiert. Wir verwenden speziell C #, dies kann jedoch für jede Sprache mit nullfähigen Typen gelten. Angenommen, Sie haben einen Wert, der ein Maximum darstellt. Dieser Maximalwert ist jedoch optional. Ich behaupte, dass eine Nullable-Zahl vorzuziehen wäre. Mein Kollege befürwortet die Verwendung von Null unter Berufung auf Präzedenzfall. Zugegeben, Dinge wie Netzwerk-Sockets haben oft Null verwendet, um eine unbegrenzte Zeitüberschreitung darzustellen. Wenn ich heute Code schreiben würde, der sich mit Sockets befasst, würde ich persönlich einen Nullwert verwenden, da ich der Meinung bin, dass dies die Tatsache besser widerspiegeln würde, dass KEINE Zeitüberschreitung vorliegt.

Welche Darstellung ist besser? Beide erfordern eine Bedingungsprüfung für den Wert "none", aber ich glaube, dass ein nullbarer Typ die Absicht ein wenig besser vermittelt.

Matt H
quelle
6
Wenn eine Zahl verwendet wird, geben Sie sie in eine Konstante ein und nicht direkt in den Code.
Renato Dinhani
@ RenatoDinhaniConceição das kann keine generelle Regel sein. Andernfalls wird am Ende alles softcodiert .
Simon Bergot

Antworten:

24

Erwägen:

  • Sprache,

  • Rahmen,

  • Kontext.

1. Sprache

Die Verwendung von ∞ kann eine Lösung für ein Maximum sein.

  • JavaScript hat zum Beispiel eine Unendlichkeit. C # nicht¹.

  • Ada hat zum Beispiel Bereiche. C # nicht.

In C # gibt es int.MaxValue, aber Sie können es in Ihrem Fall nicht verwenden. int.MaxValueist die maximale Ganzzahl von 2.147.483.647. Wenn Sie in Ihrem Code einen Maximalwert von etwas haben, z. B. einen maximal akzeptierten Druck, bevor etwas explodiert, hat die Verwendung von 2.147.483.647 keinen Sinn.

2. Framework

.NET Framework ist in diesem Punkt eher inkonsistent und die Verwendung magischer Werte kann kritisiert werden.

Gibt beispielsweise "Hello".IndexOf("Z")einen magischen Wert zurück -1. Es macht es vielleicht einfacher (oder?), Das Ergebnis zu manipulieren:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

anstatt eine benutzerdefinierte Struktur zu verwenden:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

ist aber überhaupt nicht intuitiv. Warum -1nicht -123? Ein Anfänger kann auch fälschlicherweise denken, dass 0dies auch "Nicht gefunden" bedeutet oder einfach nur "falsch" (position >= 0).

3. Kontext

Wenn Ihr Code mit Zeitüberschreitungen in Netzwerk-Sockets zusammenhängt, ist es keine schlechte Idee , etwas zu verwenden, das seit Jahrzehnten von allen verwendet wird, um konsistent zu sein . Insbesondere 0für ein Timeout ist sehr klar: Es ist ein Wert, der nicht Null sein kann. In diesem Fall kann die Verwendung einer benutzerdefinierten Klasse das Verständnis erschweren:

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • Kann ich Durationauf 0 setzen , wenn IsTimeoutEnabledes wahr ist?
  • Wenn IsTimeoutEnabledfalsch, was passiert, wenn ich Durationauf 100 setze ?

Dies kann zu mehreren Fehlern führen. Stellen Sie sich den folgenden Code vor:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

Der Vorgang dauert zehn Sekunden. Können Sie sehen, was mit diesem Code nicht stimmt, ohne die Dokumentation der TimeoutKlasse zu lesen ?

Fazit

  • nulldrückt gut die Idee aus, dass der Wert nicht hier ist. Es ist nicht vorgesehen. Nicht verfügbar. Es ist weder eine Zahl noch eine Null oder eine leere Zeichenfolge. Verwenden Sie es nicht für maximale oder minimale Werte.

  • int.MaxValuehängt stark mit der Sprache selbst zusammen. Nicht int.MaxValuefür eine maximale Geschwindigkeitsbegrenzung der VehicleKlasse oder eine maximal zulässige Geschwindigkeit für ein Flugzeug usw. verwenden.

  • Vermeiden Sie magische Werte wie -1in Ihrem Code. Sie sind irreführend und führen zu Fehlern im Code.

  • Erstellen Sie Ihre eigene Klasse, die mit den angegebenen Minimal- / Maximalwerten einfacher zu handhaben ist. Zum Beispiel haben VehicleSpeedkann VehicleSpeed.MaxValue.

  • Befolgen Sie keine vorherigen Richtlinien und verwenden Sie magische Werte, wenn es sich um eine jahrzehntelange allgemeine Konvention in einem sehr speziellen Bereich handelt, die von den meisten Leuten verwendet wird, die Code in diesem Bereich schreiben.

  • Vergessen Sie nicht, Ansätze zu mischen. Beispielsweise:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

¹ Sie können Ihren eigenen Typ erstellen, der Unendlich enthält. Ich spreche hier nur von einheimischem intTyp.

Arseni Mourzenko
quelle
1
"Es ist keine schlechte Idee, etwas zu verwenden, das seit Jahrzehnten von allen verwendet wird, um konsistent zu sein." die meisten Leute schreiben Code in diesem Feld. " - Irgendwo ist ein Tippfehler, denke ich?
Deworde
1
@deworde Ich glaube, MainMa bezieht sich auf die Richtlinien, die er selbst darüber gegeben hat.
Joshua Drake
1
Im Beispiel indexOf bin ich anderer Meinung, da -1 außerhalb des Strings liegt, was Z mit Sicherheit ist.
Joshua Drake
5
"JavaScript hat zum Beispiel eine Unendlichkeit. C # nicht." - huh?
BlueRaja - Danny Pflughoeft
+1 speziell für "Erstelle deine eigene Klasse", was ich vorgeschlagen hätte. Jedes Mal int, wenn ein Nackter nicht genug über den Typ aussagt, um das Problem einzuschränken, sollten Sie eine neue Struktur mit mehr Informationen in Betracht ziehen (const-Instanzen der Struktur, die beispielsweise magische Werte darstellen, oder eine Aufzählung, die darauf hinweist). Oder erwägen Sie die Programmierung von Verträgen oder andere Lösungen, aber ich denke, eine benutzerdefinierte Struktur ist am einfachsten.
CodexArcanum
12

Null ist nicht besser als eine magische Zahl.

Das Wichtigste ist, die Werte mit magischen Effekten zu NAMEN, wenn Sie solche Werte haben müssen, und sicherzustellen, dass die Definitionen dieser Namen für jeden sichtbar sind, der auf den magischen Wert und die WTFs stößt.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!
mjfgates
quelle
2
Ok, vielleicht hängt es mehr von der Sprache ab, aber in C # würden Sie wahrscheinlich tun: if (timeout.HasValue) anstelle eines direkten Vergleichs mit null.
Matt H
2
Null ist nicht schlechter als eine magische Zahl. Bei magischen Zahlen weiß man nie, was die magische Zahl ist ... es kann 0, -1 oder etwas anderes sein. null ist einfach null.
marco-fiset
9
Null bedeutet Wertlosigkeit. Dies ist das Konzept, das viele magische Zahlen zum Ausdruck bringen wollen. Die Verwendung von null mit einem nullfähigen Typ ist eine VIEL bessere Lösung, als einen beliebigen Wert aus dem Bereich der möglichen Werte für einen Datentyp auszuwählen.
17 von 26
2
"Null" als magischen Wert zu verwenden, wenn Ihr Typ "null" hat, ist in Ordnung. Das Wichtigste ist, NAME es, weil sicher, als Shootin 'der nächste Kerl mitkommen wird nicht wissen, was Sie meinten. Null kann "unendlich", "noch nicht angegeben", "Fehler im Code, der die Datenstruktur erstellt hat" oder eine beliebige Anzahl anderer Dinge bedeuten. Nur ein Name gibt dem nächsten Codierer an, dass Sie diesen Wert als vorhanden angesehen haben und welches Verhalten Sie als auslösend angesehen haben.
mjfgates
1
@CodeInChaos: Ich weiß, dass Sie beides können, aber ich bevorzuge HasValue. Eigentlich bin ich kein großer Fan von null im Allgemeinen, aber nullfähige Typen, die HasValue verwenden, fühlen sich ein wenig näher an einer Option / Vielleicht schreiben Sie mir, von der ich ein Fan bin.
Matt H
10

MAGIC_NUMBERCode sollte nach Möglichkeit unbedingt vermieden werden. nullist ein viel klarerer Ausdruck der Absicht.

DeadMG
quelle
6

In C # haben viele CLR-Klassen einen statischen EmptyMember:

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

So müssen Sie sich nicht merken, ob Sie einen magischen Wert oder null verwenden müssen, um ein leeres Objekt zu erstellen.

Aber was ist, wenn Sie es mit einem einfachen Werttyp wie einem zu tun haben int? Überlegen Sie in diesem Fall, ob Sie Opfer der primitiven Besessenheit werden . Es ist durchaus möglich, dass Ihre scheinbar einfache numerische Eigenschaft von einer eigenen Klasse oder Struktur profitiert, mit der Sie das Element angeben Emptyund auch ein anderes Verhalten hinzufügen können, das für diese Art von Wert spezifisch ist.

Kyralessa
quelle
3

In diesem Fall ist der Nullwert ein guter Hinweis darauf, dass es kein Maximum gibt. Wenn der Sonderfall bedeutet, dass der betreffende Wert nicht zutrifft und Sie die konfigurierte Funktion einfach nicht möchten, ist null ein gutes Indiz dafür.

Ein Problem bei der Verwendung von Null zur Darstellung von Sonderfällen besteht darin, dass es nur einen Nullwert gibt und möglicherweise mehrere Sonderfälle vorliegen. In diesem Fall würde ich eine Aufzählung als zusätzlichen Parameter übergeben, der einen Sonderfall anzeigen oder den int-Wert normal verwenden kann. (Dies ist im Wesentlichen das, was das Nullable <> für Sie bewirkt, obwohl es einen Booleschen Wert anstelle einer Aufzählung verwendet und die Parameter in einer einzigen Struktur kombiniert.)

JGWeissman
quelle
3

In diesem Fall ist ein nullbarer Typ meiner Meinung nach vollkommen sinnvoll.

Null bedeutet Wertlosigkeit. Dies ist ein deutlich anderes Konzept als eine Zahl mit dem Wert 0.

Wenn Sie "Wenn ich Ihnen keinen Wert gebe, verwenden Sie das Maximum" sagen möchten, ist die Übergabe von Null genau die richtige Art, dies auszudrücken.

17 von 26
quelle
1

Null: Häufiger Fehlerwert, nicht angegeben, ungültig oder nicht vorhanden.

Null: Ein tatsächlicher, aber nicht notwendigerweise logischer oder intuitiver Wert (in diesem Zusammenhang). Auch ein allgemeiner Wert bei der Initialisierung.

Im Zusammenhang mit Ihrem Problem ist die timeoutInMillisecondsEigenschaft optional, und es wird nicht erwähnt, dass der Overhead dieses Ansatzes sie als Option disqualifizieren würde.

Schlussfolgerung: Es gibt Ausnahmen, und die Lösungen variieren je nach Sprache und Bereich. In diesem Fall würde ich Null wählen. Ich glaube, manche Leute verstehen das falsch, wenn sie die Daten nicht gut von der Schnittstelle trennen. Sie erwarten lediglich, dass jeder Client die Dokumentation (oder Implementierung) liest, um zu bestimmen, wie diese speziellen Werte verwendet / gehandhabt werden sollen - die speziellen Fälle dringen in das Programm des Clients ein und es kann ziemlich unklar sein. Durch Hinzufügen einer guten Abstraktionsschicht kann die Verwendung viel klarer werden.

Justin
quelle
0

Null ist schlechter zu benutzen als MagicNumber. Null repräsentiert die Idee besser ausgedrückt, aber es ist nicht konsistent zwischen den Plattformen, wie es sich verhält, es MagicNumberfunktioniert immer gleich, was von Vorteil ist.

je nach verwendeter umgebung / sprache könnte null sein

  • sei einfach 0
  • kann kein legaler Wert sein
  • kann aufgrund der Drei-Wege-Logik zu unerwarteten Ergebnissen führen

MagicNumber verhält sich immer gleich

Ryathal
quelle
0

Wenn Sie vergessen, nach der magischen Zahl zu suchen (was richtig sein wird), wird eine magische Zahl mit unsinnigen Daten für eine Weile fortgeführt. Es ist viel besser, eine Null zu haben, die so schnell wie möglich eine Ausnahme verursacht.

Tom Hawtin - Tackline
quelle
-1

Null ist nicht die einzige Alternative zu einer magischen Zahl.

public static int NO_TIMEOUT = 0;  // javaish

Null ist böse. Im obigen Beispiel könnten Sie damit durchkommen, da der Code offensichtlich mit einer Null umgehen kann. Im Allgemeinen passiert es jedoch, dass Sie früher oder später eine Nullzeigerausnahme erhalten, wenn Sie damit beginnen, Nullen weiterzugeben. Es kann sein, dass dies beim ersten Schreiben des Codes nicht der Fall ist, der Code bleibt jedoch weitaus länger als bei der ersten Version erhalten. Es wird oft von Leuten gepflegt, die nicht so viel über das System wissen wie die ursprünglichen Entwickler.

Scala (zum Beispiel) hat eine nette Alternative in der Optionsklasse. Die Option-Klasse hat einen von zwei Werten: Some (Einige) - umschließt den gewünschten Wert und None (Keine) - ohne Wert.

Das macht es für jeden Entwickler offensichtlich, dass es möglicherweise keinen Wert gibt und Sie einen besseren Code dafür hatten. Nun, es sollte es sowieso offensichtlich machen.

Und nicht alle magischen Zahlen sind ein Problem. Abhängig vom Kontext können 0, 1, 1024 usw. alle offensichtlich sein. 347? Ja, das solltest du vermeiden. :-)

Jon Strayer
quelle
4
-1: Begründe "null ist böse".
Deworde
4
Das Definieren eines Alias ​​für eine Zahl ändert nichts an der Tatsache, dass es sich immer noch um eine magische Zahl handelt.
17 von 26
Vielleicht haben Sie eine andere Definition der magischen Zahl als ich. Bitte sehen Sie en.wikipedia.org/wiki/…
Jon Strayer
1
Ich stimme Jon Strayer hier zu. Null ist ein Beispiel für ein ADT in einer Sprache, die ADTs nicht unterstützt. Das OP kann hier wahrscheinlich durchkommen, aber im Allgemeinen halte ich jede Sprache, die null hat, für ein Versagen der Programmierer.
Jeremy Wall