Beachten Sie, dass es keinen Grund für eine Warnung beim Anruf an gibt Console.WriteLine()
. Die Referenztyp-Eigenschaft ist kein nullfähiger Typ, sodass der Compiler nicht warnen muss, dass sie möglicherweise null ist.
Sie könnten argumentieren, dass der Compiler vor der Referenz in sich struct
selbst warnen sollte . Das erscheint mir vernünftig. Aber das tut es nicht. Dies scheint eine Lücke zu sein, die durch die Standardinitialisierung für Werttypen verursacht wird, dh es muss immer einen Standardkonstruktor (ohne Parameter) vorhanden sein, der immer nur alle Felder auf Null setzt (Nullen für Referenztypfelder, Nullen für numerische Typen usw.). ).
Ich nenne es eine Lücke, weil theoretisch nicht nullbare Referenzwerte eigentlich immer nicht null sein sollten! Duh. :) :)
Diese Lücke scheint in diesem Blog-Artikel behoben zu werden: Einführung in nullable Referenztypen in C #
Vermeiden von Nullen Bisher ging es bei den Warnungen darum, Nullen in nullbaren Referenzen vor einer Dereferenzierung zu schützen. Die andere Seite der Medaille besteht darin, zu vermeiden, dass die nicht stornierbaren Referenzen Nullen enthalten.
Es gibt verschiedene Möglichkeiten, wie Nullwerte entstehen können, und die meisten von ihnen sind eine Warnung wert, während einige von ihnen ein weiteres „Meer von Warnungen“ verursachen würden, das besser zu vermeiden ist:
…
- Verwenden des Standardkonstruktors einer Struktur mit einem Feld vom nicht löschbaren Referenztyp. Dieser ist hinterhältig, da der Standardkonstruktor (der die Struktur auf Null setzt) an vielen Stellen sogar implizit verwendet werden kann. Wahrscheinlich ist es besser, nicht zu warnen [Hervorhebung meiner - PD] , sonst würden viele vorhandene Strukturtypen unbrauchbar.
Mit anderen Worten, ja, das ist eine Lücke, aber nein, es ist kein Fehler. Die Sprachdesigner sind sich dessen bewusst, haben sich jedoch entschieden, dieses Szenario aus den Warnungen herauszulassen, da dies angesichts der Funktionsweise der struct
Initialisierung unpraktisch wäre .
Beachten Sie, dass dies auch im Einklang mit der breiteren Philosophie steht, die hinter der Funktion steht. Aus dem gleichen Artikel:
Wir möchten, dass es sich über Ihren vorhandenen Code beschwert. Aber nicht widerlich. So werden wir versuchen, dieses Gleichgewicht herzustellen:
…
- Es gibt keine garantierte Nullsicherheit [Schwerpunkt Mine - PD] , selbst wenn Sie auf alle Warnungen reagieren und diese beseitigen. Es gibt viele Lücken in der Analyse nach Notwendigkeit und auch einige nach Wahl.
Bis zu diesem letzten Punkt: Manchmal ist eine Warnung die „richtige“ Vorgehensweise, wird jedoch ständig auf vorhandenen Code ausgelöst, selbst wenn dieser tatsächlich auf null sichere Weise geschrieben ist. In solchen Fällen irren wir auf der Seite der Bequemlichkeit, nicht der Korrektheit. Wir können kein „Meer von Warnungen“ für vorhandenen Code ausgeben: Zu viele Leute würden die Warnungen einfach wieder ausschalten und niemals davon profitieren.
Beachten Sie auch, dass dasselbe Problem bei Arrays von nominell nicht nullbaren Referenztypen (z string[]
. B. ) besteht. Wenn Sie das Array erstellen, sind alle Referenzwerte gültig. null
Dies ist jedoch zulässig und generiert keine Warnungen.
Soviel zur Erklärung, warum die Dinge so sind, wie sie sind. Dann stellt sich die Frage, was man dagegen tun soll. Das ist viel subjektiver und ich glaube nicht, dass es eine richtige oder falsche Antwort gibt. Das gesagt…
Ich persönlich würde meine struct
Typen von Fall zu Fall behandeln. Für diejenigen, bei denen die Absicht tatsächlich ein nullbarer Referenztyp ist, würde ich die ?
Anmerkung anwenden . Sonst würde ich nicht.
Technisch gesehen sollte jeder einzelne Referenzwert in a struct
"nullbar" sein, dh die ?
nullbare Annotation mit dem Typnamen enthalten. Aber wie bei vielen ähnlichen Funktionen (wie async / await in C # oder const
in C ++) hat dies einen "ansteckenden" Aspekt, da Sie diese Annotation entweder später überschreiben müssen (mit der !
Annotation) oder eine explizite Nullprüfung einschließen müssen oder weisen Sie diesen Wert immer nur einer anderen nullfähigen Referenztypvariablen zu.
Für mich hat dies einen großen Nachteil darin, nullfähige Referenztypen zu aktivieren. Da solche Mitglieder von struct
Typen ohnehin irgendwann eine Sonderfallbehandlung erfordern und die einzige Möglichkeit, sie wirklich sicher zu behandeln, während sie weiterhin nicht nullfähige Referenztypen verwenden können, darin besteht, überall, wo Sie die verwenden struct
, Nullprüfungen durchzuführen , bin ich der Meinung Es ist eine vernünftige Implementierungsentscheidung, zu akzeptieren, dass der Code bei der Initialisierung des struct
Codes dafür verantwortlich ist, dies korrekt zu tun und sicherzustellen, dass das nicht nullbare Referenztypelement tatsächlich auf einen nicht nullwert initialisiert wird.
Dies kann durch die Bereitstellung eines "offiziellen" Initialisierungsmittels unterstützt werden, z. B. eines nicht standardmäßigen Konstruktors (dh eines mit Parametern) oder einer Factory-Methode. Es besteht immer noch das Risiko, dass der Standardkonstruktor oder überhaupt kein Konstruktor verwendet wird (wie bei Array-Zuweisungen). Durch die Bereitstellung einer bequemen Möglichkeit zur struct
korrekten Initialisierung des Codes wird jedoch Code gefördert, der ihn verwendet, um Nullreferenzen in Nicht- zu vermeiden nullfähige Variablen.
Wenn Sie jedoch 100% ige Sicherheit in Bezug auf nullfähige Referenztypen wünschen, besteht der richtige Ansatz für dieses bestimmte Ziel eindeutig darin, jedes Referenztypelement in einem struct
mit zu kommentieren ?
. Dies bedeutet, dass jedes Feld und jede automatisch implementierte Eigenschaft zusammen mit jeder Methode oder jedem Eigenschafts-Getter, der solche Werte oder das Produkt solcher Werte direkt zurückgibt. Dann muss der konsumierende Code an jedem Punkt, an dem solche Werte in nicht nullfähige Variablen kopiert werden, Nullprüfungen oder den Nullverzeihungsoperator enthalten.