Lassen Sie uns zunächst die Frage klären.
In C# 1.0 hatten wir zwei breite Kategorien von Typen: Werttypen, die niemals null sind, und Referenztypen, die nullfähig sind. *
Wert und Referenztypen unterstützen einen Mitglied Zugriffsoperator, .
, die einen Wert mit einer Instanz zugeordnete auswählt.
Für Referenztypen ist die Beziehung zwischen dem .
-Operator und der NULL-Zulässigkeit des Empfängers: Wenn der Empfänger eine Nullreferenz ist, dann erzeugt die Verwendung des .
-Operators eine Ausnahme. Da C# 1.0-Werttypen überhaupt nicht nullfähig sind, müssen Sie nicht angeben, was passiert, wenn Sie .
einen Nullwerttyp eingeben. sie existieren nicht.
In C# 2.0 wurden Nullwertwerte hinzugefügt. Nullable-Werttypen sind nichts Magisches, solange ihre In-Memory-Repräsentation geht; es ist nur eine Struktur mit einer Instanz des Wertes und einem Bool, ob es null ist oder nicht.
Es gibt einige Compiler Magie (**
) obwohl seit Nullable Werte Typen mit heben Semantik kommen.Unter heben wir an, wir meinen, dass Operationen auf Nullable-Werttypen die Semantik haben: "Wenn der Wert nicht null ist, dann führe die Operation auf den Wert aus und konvertiere das Ergebnis in einen nullbaren Typ; andernfalls ist das Ergebnis null". (***
)
Das heißt, wenn wir
int? x = 2;
int? y = 3;
int? z = null;
int? r = x + y; // nullable 5
int? s = y + z; // null
Hinter den Kulissen haben die Compiler alle Arten von Magie tun effizient angehoben Arithmetik zu implementieren; see my lengthy blog series on how I wrote the optimizer if this subject interests you.
Der Operator .
ist jedoch nicht aufgehoben. Es könnte sein! Es gibt mindestens zwei mögliche Konstruktionen, die Sinn machen:
nullable.Whatever()
als Nullable-Referenztypen verhalten könnten Sie: eine Ausnahme aus, wenn der Empfänger null ist, oder
- es wie nullable Arithmetik verhalten könnte: wenn
nullable
null ist dann ist der Aufruf an Whatever()
elided und das Ergebnis ist ein Null von welchem Typ Whatever()
zurückgegeben worden wäre.
Die Frage ist also:
Warum .Value.
erfordern, wenn es ein vernünftiger Entwurf für den .
Operator ist nur Arbeit und ein Mitglied des zugrunde liegenden Typs zu extrahieren?
Gut.
Beachten Sie, was ich gerade dort getan habe. Es gibt zwei Möglichkeiten, die beide vollkommen Sinn machen und mit einem etablierten, gut verstandenen Aspekt der Sprache übereinstimmen, und sie widersprechen einander. Sprachdesigner finden sich in diesem Cleft Stick die ganze Zeit. Wir befinden uns jetzt in einer Situation, in der es völlig nicht offensichtlich ist, ob sich .
auf einem Nullwerttyp wie .
auf einem Referenztyp verhalten sollte oder ob .
sich wie +
auf einem nullable int verhalten sollte. Beides ist plausibel. Welcher auch immer ausgewählt wird, jemand wird denken, dass es falsch ist.
Das Sprachen-Design-Team erwog Alternativen wie explizite. Zum Beispiel der "Elvis" ?.
-Operator, der explizit ein aufgehobener Zugriff auf NULL-Member ist. Dies wurde für C# 2.0 in Betracht gezogen, aber zurückgewiesen und schließlich zu C# 6.0 hinzugefügt. Es wurden einige andere syntaktische Lösungen in Betracht gezogen, aber alle wurden aus Gründen abgelehnt, die der Geschichte entgangen waren.
Schon sehen wir, dass wir ein potenzielles Design-Minenfeld für .
auf Werttypen haben, aber warten Sie, es wird schlechter.
Lassen Sie uns nun einen weiteren Aspekt der .
berücksichtigen, wenn auf einen Wert Typ angewendet: Wenn der Wert Art der schreckliche wandelbaren Werttyp und das Element ist ein Feld ist, dann ist x.y
eine Variable wenn x
ist eine Variable und ansonsten ein Wert. Das heißt, x.y = 123
ist zulässig, wenn x
eine Variable ist.Aber wenn x
keine Variable ist, dann verbietet der C# -Compiler die Zuweisung, weil die Zuordnung zu einer Kopie des Werts vorgenommen würde.
Wie verhält es sich mit Nullwerttypen? Wenn wir einen Nullable-wandelbaren Werttyp X?
dann was macht
x.y = 123
tun? Denken Sie daran, x
wirklich ist eine Instanz des unveränderlichen Typs Nullable<X>
, also, wenn dies bedeutet, x.Value.y = 123
dann wir die Kopie des Wertes zurückgegeben werden, mutiert von der Value
Eigenschaft, die sehr scheint, sehr falsch.
Was machen wir also? Sollten NULL-Werttypen selbst veränderbar sein? Wie würde diese Mutation funktionieren? Ist es eine Copy-in-Copy-out-Semantik? Das würde bedeuten, dass ref x.y
illegal wäre, weil ref
eine Variable verlangt, keine Eigenschaft.
Es wird eine riesige freakin 'Chaos sein.
In C# 2.0 versuchte das Designteam, Generics zu der Sprache hinzugefügt; Wenn Sie jemals versucht haben, Generika zu einem bestehenden System hinzuzufügen, wissen Sie, wie viel Arbeit das ist. Wenn Sie nicht, nun, es ist eine Menge Arbeit. Ich denke, dass das Designteam einen Pass erhalten kann, wenn es darum geht, sich für all diese Probleme zu entscheiden und .
keine spezielle Bedeutung für Nullable Value-Typen zu haben. "Wenn Sie den Wert möchten, dann rufen Sie .Value
" hat den Vorteil, keine besondere Arbeit seitens des Design-Teams zu erfordern! Und ähnlich, "wenn es weh tut, veränderbare Nullwerttypen zu verwenden, dann höre vielleicht damit auf", sind niedrige Kosten für die Designer.
Wenn wir in einer perfekten Welt leben, dann würden wir Arten von Typen in C# 1.0 hatten zwei orthogonal haben: Referenztypen vs Werttypen und Nullable Types vs Nicht-Nullable-Typen. Was wir erhielten, war Nullwert Referenztypen und nicht-Nullable Werttypen in C# 1.0, Nullable Werttypen in C# 2.0, und ein bisschen Null Referenz Typen in C# 8.0, ein Jahrzehnt und eine Hälfte später.
In dieser perfekten Welt hätten wir alle Operator Semantik, Aufhebung Semantik, Variable Semantik, und so weiter auf einmal aussortiert, um sie konsistent zu machen.
Aber, hey, wir leben nicht in dieser perfekten Welt. Wir leben in einer Welt, in der das Perfekte der Feind des Guten ist, und Sie müssen .Value.
anstelle von .
in C# 2.0 bis 5.0 und ?.
in C# 6.0 sagen.
*
Ich ignoriere bewusst Zeiger-Typen, die NULL-Werte zulassen und einige Eigenschaften von Werttypen haben und einige Eigenschaften von Referenztypen und die haben ihre eigenen speziellen Operatoren für dereferencing und Mitglied Zugang.
**
Es gibt auch Magie in Sachen wie: Nullable Werttypen erfüllen nicht die Werttyp-Einschränkung, Nullable Werttypen Box zu Null Referenzen oder boxed zugrunde liegenden Typen und viele andere kleine spezielle Verhaltensweisen. Aber das Speicherlayout ist nichts Magisches; es ist nur ein Wert neben einem Bool.
***
Funktionelle Programmierer werden natürlich wissen, dass dies die Bindeoperation auf der vielleicht Monade ist.
Äh, wie sonst würden Sie den Wert lesen? – maccettura
Was schlagen Sie stattdessen vor? – Servy
Warum können Sie nicht direkt in das Objekt eintauchen? –