In einem C # 8-Projekt mit aktivierten nullfähigen Referenztypen habe ich den folgenden Code, der meiner Meinung nach eine Warnung vor einer möglichen Null-Dereferenzierung geben sollte, aber nicht:
public class ExampleClassMember
{
public int Value { get; }
}
public struct ExampleStruct
{
public ExampleClassMember Member { get; }
}
public class Program
{
public static void Main(string[] args)
{
var instance = new ExampleStruct();
Console.WriteLine(instance.Member.Value); // expected warning here about possible null dereference
}
}
Wenn instance
mit dem Standard - Konstruktor initialisiert wird, instance.Member
wird auf den Standardwert gesetzt ExampleClassMember
, das ist null
. Somit instance.Member.Value
wird NullReferenceException
zur Laufzeit ein geworfen . Da ich die Nullfähigkeitserkennung von C # 8 verstehe, sollte ich eine Compiler-Warnung über diese Möglichkeit erhalten, aber ich weiß nicht; warum ist das so?
c#
nullable
c#-8.0
nullable-reference-types
DylanSp
quelle
quelle
ExampleStruct
vonstruct
zu wechsleclass
.Nullable
Antworten:
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 #
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:
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 # oderconst
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 verwendenstruct
, Nullprüfungen durchzuführen , bin ich der Meinung Es ist eine vernünftige Implementierungsentscheidung, zu akzeptieren, dass der Code bei der Initialisierung desstruct
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.quelle
Angesichts der hervorragenden Antwort von @ peter-duniho scheint es ab Oktober 2019 am besten zu sein, alle Mitglieder ohne Werttyp als nullfähige Referenz zu markieren.
quelle