25. Mär 2022
Lesedauer 10 Min.
Nullzeiger-Probleme vermeiden
Nullzeigerprüfung von .NET 6
Mit NET 6 beziehungsweise C# 8 ist Microsoft angetreten, mögliche Nullzeiger-Dereferenzierungen bereits zur Kompilierzeit zu erkennen.

Die Idee ist, vermittels statischer Codeanalyse dem Entwickler genügend Unterstützung anzubieten, um Nullzeiger-Probleme gar nicht erst in das fertige Produkt einfließen zu lassen. Wie das in der Praxis funktioniert und wo sich diese von der Theorie unterscheidet, zeigt der nachfolgende Artikel. Außerdem gibt er wertvolle Hinweise für die Migration bestehender Codebasen.Nullzeiger werden gelegentlich auch als der Billion-Dollar-Mistake bezeichnet. Diese Bezeichnung geht zurück auf Tony Hoare, der 1965 den Nullzeiger erfand und – so seine Aussage – dadurch viel Frustration über Entwickler und Anwender gebracht haben dürfte. Ob man sich dieser Argumentation anschließen möchte, sei jedem selbst überlassen. Fakt ist jedenfalls, dass jeglicher Fehler, der sich erst spät im Prozeß manifestiert, grundsätzlich immer teurer ist. Insofern ist es eine gute Sache, dass Compiler und IDE nun in der Lage sind, dem Entwickler mögliche Fehlerquellen bereits zur Kompilierzeit oder beziehungsweise bereits während des Codierens aufzuzeigen.Laut Spezifikation ist das übergeordnete Ziel, die Chance zu minimieren, dass die Applikation zur Laufzeit anlässlich der Dereferenzierung eines Nullzeigers eine NullReferenceException wirft. Erreicht wird dies einerseits durch einige Anpassungen in der Sprachspezifikation, andererseits vermittels statischer Codeanalyse. Die syntaktischen Änderungen sollen den Entwickler in die Lage versetzen, in seinem Code die Intention besser herauszuarbeiten.
Visual Studio 2022 mit Nullüberprüfung
Für neue Projekte ist ab Visual Studio 2022 die Nullüberprüfung standardmäßig aktiviert. Da diese neue Semantik das Potential hat, in bestehenden Projekten unter Umständen für wirklich sehr viele Warnungen zu sorgen, wird es hier nicht automatisch durch die Entwicklungsumgebung aktiviert. Man kann aber in den Projekteinstellungen jederzeit das gewünschte Verhalten manuell festlegen.Zusätzlich läßt sich die Nullprüfung auf Quellcodebasis über #nullable <wert> steuern. Unter https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references gibt es eine ausführliche Tabelle, die genau beschreibt, welche der möglichen Einstellungen was genau bewirkt. Gerade für die Umstellung einer größeren Codebasis ist es eine gute Idee, sich eine sinnvolle Strategie zurechtzulegen - das simple projektweite Einschalten der Prüfung wird mit einiger Sicherheit eine erkleckliche Anzahl Warnungen zum Vorschein bringen, die je nach Projektgröße auch schon mal dreistellig sein kann.Genug Theorie - jetzt zur Praxis
Der üblichen Deklaration einer Membervariablen sieht man im Falle eines Referenztypen nicht an, ob aus algorithmischer Sicht der Wert null ein gültiger und erwartungskonformer Wert sein soll oder eher unerwartet ist. Mit dem Einschalten der Prüfung lautet der neue Vorgabewert für Referenztypen nicht nullable, folglich muss jeder erwünschte null-fähige Referenztyp explizit als solcher gekennzeichnet werden:
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> </span>{
<span class="hljs-keyword">public</span> TreeNode <span class="hljs-keyword">Parent</span>;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">List</span><TreeNode> Childs;
}
Der Algorithmus für die obige Klasse sieht nun vor, dass alle Nodes immer eine Childs-Liste haben sollen, die natürlich auch leer sein kann, aber nie null. Anders sieht die Sachlage mit dem Feld Parent aus, denn der oberste Knoten eines Baumes hat keinen Parent, folglich ist null hier ein zulässiger Zustand. Um dieses erwünschten Verhalten deklarativ festzuschreiben, ändert man man die Deklaration ab, indem man ganz analog zu den bekannten Nullable Types einfach ein Fragezeichen an den Typ anhängt:
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> </span>{
<span class="hljs-keyword">public</span> TreeNode? <span class="hljs-keyword">Parent</span>; <span class="hljs-comment">// allow null</span>
<span class="hljs-comment"> public List<TreeNode> Childs; // never null</span>
<span class="hljs-comment">}</span>
<span class="hljs-comment"> </span>
Dies teilt der Codeanalyse mit, dass eine Zuweisung von null an das Feld Parent durchaus erwartungskonform ist, bei der Childs-Liste hingegen nicht. Ebenso ist damit deklarativ abgesichert, dass die Liste keine Nullzeiger enthalten soll - wäre das erwünscht, müsste der Elementetyp ebenso ein Fragezeichen erhalten. Mit dieser Deklaration müsste folgender Code nun also irgendeine Reaktion des Compilers provozieren:
var <span class="hljs-keyword">node</span> <span class="hljs-title">= new</span> TreeNode();
<span class="hljs-keyword">node</span>.<span class="hljs-title">Childs</span>.Add( new TreeNode());
<span class="hljs-keyword">node</span>.<span class="hljs-title">Childs</span>.Add( null);
Wie in Bild 1 zu sehen ist, informiert uns der Compiler in der Tat gleich mit mehreren Hinweisen: Dem aufmerksamen Betrachter fällt auf, dass es sich in der Tat nur um Warnungen handelt. Der Code lässt sich also nach wie vor kompilieren und auch ausführen, mit den üblichen Folgen einer NullReferenceException zur Laufzeit. Möchte man gezielt nur die Ausgaben der Nullzeiger-Analyse zu Compiler-Fehlern erklären, ist dies über zwei kleine Tags in der *.csproj-Datei möglich:
<span class="hljs-tag"><<span class="hljs-name">Project</span> <span class="hljs-attr">...</span>></span>
<span class="hljs-tag"><<span class="hljs-name">PropertyGroup</span>></span>
<span class="hljs-tag"><<span class="hljs-name">TargetFramework</span>></span> … etc ...
<span class="hljs-tag"><<span class="hljs-name">Nullable</span>></span>enable<span class="hljs-tag"></<span class="hljs-name">Nullable</span>></span>
<span class="hljs-tag"><<span class="hljs-name">WarningsAsErrors</span>></span>Nullable
<span class="hljs-tag"></<span class="hljs-name">WarningsAsErrors</span>></span>
<span class="hljs-tag"></<span class="hljs-name">PropertyGroup</span>></span>
<span class="hljs-tag"></<span class="hljs-name">Project</span>></span>
Wie bereits erläutert, aktiviert das Tag <Nullable> die Nullprüfung, <WarningsAsErrors> sorgt dafür, dass diese Warnungen den Status Fehler erhalten und den Kompiliervorgang im Falle erkannter Probleme erfolglos abbrechen lassen.

Die Nullüberprüfungwarnt vor allerlei Fehlermöglichkeiten(Bild 1)
Geyer
Zurück zu den beiden Fehlermeldungen. Die zweite Meldung, die sich auf Childs.Add(null) bezieht, leuchtet ein und ist auch das erwartete Ergebnis. Aber warum beschwert sich der Compiler über Childs? Wie schon festgestellt, beruht die Prüfung auf einer statischen Analyse des Quellcodes. Da wir die Variable Childs im Gegensatz zu Parent eben nicht als null-zulässig deklariert haben, erwartet der Compiler von uns auch, dass der Variable spätestens im Konstruktor ein Wert ungleich null zugewiesen wird. Eine mögliche Korrektur wäre also, die Liste bereits bei der Deklaration zu initialisieren:
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> </span>{
<span class="hljs-keyword">public</span> TreeNode? <span class="hljs-keyword">Parent</span>;
<span class="hljs-keyword">public</span> readonly <span class="hljs-keyword">List</span><TreeNode> Childs = <span class="hljs-keyword">new</span>();
}
Im weiteren Verlauf des Quellcodes taucht nun das folgende Stück Code auf:
<span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> {
<span class="hljs-keyword">public</span> TreeNode? Parent;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">readonly</span> List<TreeNode> Childs = <span class="hljs-keyword">new</span>();
<span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> NumChilds = <span class="hljs-number">0</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AddChild</span>(<span class="hljs-params"> TreeNode node</span>) </span>
<span class="hljs-function"> </span>{
Childs.Add(node);
node.Parent = <span class="hljs-keyword">this</span>;
NotifyChildsAdded(<span class="hljs-number">1</span>);
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">NotifyChildsAdded</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> delta</span>)</span>
<span class="hljs-function"> </span>{
NumChilds += delta;
Parent.NotifyChildsAdded(delta);
}
}
Der Compiler beanstandet hier sofort und völlig zu Recht, dass in der Methode NotifyChildsAdded() die Variable Parent ohne Prüfung verwendet wird und weist uns folgerichtig darauf hin, dass hier eine mögliche Nullzeiger-Exception lauert. Korrigieren wir den Code, wird die statische Codeanalyse diese Prüfung bemerken und schlussfolgern, dass die Verwendung von Parent nun sicher sein müsse, weswegen die Warnung auch prompt verschwindet:
<span class="hljs-keyword">class</span> <span class="hljs-title">TreeNode</span> {
...
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">NotifyChildsAdded</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> delta</span>)</span>
<span class="hljs-function"> </span>{
NumChilds += delta;
<span class="hljs-keyword">if</span>( Parent != <span class="hljs-literal">null</span>)
Parent.NotifyChildsAdded(delta);
}
}
Man beachte, dass der Zugriff auf die Liste Childs in der Methode AddChilds() unbeanstandet blieb, da wir ja deklarativ und durch entsprechenden Code abgesichert haben, dass Childs niemals null sein darf. Was passiert nun aber, wenn wir Childs einfach mal explizit und in voller Absicht den Wert null verpassen:
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">ClearList</span><span class="hljs-params">()</span> </span>{
Childs = <span class="hljs-keyword">null</span>;
}
Tatsächlich wird die Nullüberprüfung diese Zuweisung unmittelbar als illegal beanstanden, tut dies aber wiederum per Warnung. Wenn wir den Code dennoch kompilieren und die Methode aufrufen, wird es beim nächsten Zugriff auf Childs auch die übliche NullReferenceException geben.Was die Nullüberprüfung also leisten kann, ist dem Entwickler Unterstützung beim Codieren zu geben und auf vergessene Nullprüfungen und Initialisierungen hinzuweisen. Durch die Forderung, dass Variablen passend annotiert werden müssen, um das System optimal zu nutzen, wird der Entwickler darüber hinaus dazu gebracht, sich mit dem Aspekt von zulässigen oder unzulässigen Nullwerten in seinem Code zu befassen. Im Idealfall wird man sich bei jeder neuen Variable fürderhin fragen, ob man Nullwerte von vornherein ausschließen möchte oder ob die mögliche Zuweisung oder Rückgabe von null als algorithmisches Hilfsmittel einen Zweck erfüllen soll.Es geht hier wie gesagt nicht darum, dass der Einsatz von null generell irgendwie schlechter Code wäre. Es gibt durchaus legitime Einsatzfälle, wo null einen Zweck erfüllt, den man bei einem kompletten Verzicht auf null nur sehr schlecht und mit zusätzlichem Aufwand erreichen könnte. Wenn uns C# also bei der Umstellung existierender Projekte mit einigen hundert Warnungen beglückt, ist dies letztlich nur die Aussage »Hey Entwickler, du hast da ein paar Stellen in deinem Quelltext, über die wir reden sollten. Schau dir das mal an und lege fest, wo wir null akzeptieren wollen und wo nicht. Ich sage dir dann, wo dein Code inkonsequent geschrieben ist und wir fixen das gemeinsam.«Um die Nullprüfung besser kontrollieren zu können, hat Microsoft auch eine Handvoll neuer Attribute spendiert. Schauen wir uns dazu den Code im folgenden Listing an:
<span class="hljs-keyword">class</span> <span class="hljs-title">TreeNodeFactory</span>
{
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">CreateNode</span>(<span class="hljs-params"> <span class="hljs-keyword">string</span> name, <span class="hljs-keyword">out</span> TreeNode child</span>)</span>
<span class="hljs-function"> </span>{
<span class="hljs-keyword">if</span>(<span class="hljs-keyword">string</span>.IsNullOrEmpty(name))
child = <span class="hljs-keyword">new</span> TreeNode(name);
<span class="hljs-keyword">else</span>
child = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">return</span> (child != <span class="hljs-literal">null</span>);
}
}
Problematisch ist hier der Ausgabeparameter child. Es kann vorkommen, dass das Hinzufügen fehlschlägt, zum Beispiel wegen illegaler Eingabedaten. Dann liefert die Funktion zwar false, allerdings muss ja trotzdem auch dem out-Parameter irgend etwas zugewiesen werden. Da wir keine Instanz erstellt haben, liefern wir hier null und müssen dazu aber folgerichtig den Parameter als nullable deklarieren:
<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">CreateNode</span>(<span class="hljs-params"> ..., <span class="hljs-keyword">out</span> TreeNode? child</span>)</span>
<span class="hljs-function"> </span>
Das funktioniert so zwar grundsätzlich, ist aber aus anderen Gründen dennoch recht unschön. Der Effekt ist nämlich, dass der Aufrufer jetzt wie folgt codieren muss, um die ansonsten allfällige Warnung zu umgehen:
<span class="hljs-keyword">if</span>( root.CreateNode(<span class="hljs-string">"foobar"</span>, our <span class="hljs-keyword">var</span> child))
<span class="hljs-keyword">if</span>( child != <span class="hljs-literal">null</span>) <span class="hljs-comment">// zusätzliche Prüfung </span>
<span class="hljs-comment"> root.AddChild( child);</span>
<span class="hljs-comment"> </span>
Die zusätzliche Nullprüfung dient hier einzig und allein dem Zweck, dem Compiler zu erklären, dass wird AddChild() keinesfalls mit einem Nullzeiger aufrufen werden. Nun ist die Sache aber so, dass das tatsächlich nie passieren kann, weil CreateNode() dann ja sowieso false abliefern würde, und ebendies prüfen wir ja ab.In solchen Fällen gibt es eine ganze Reihe möglicher Lösungen. Der offensichtlichste ist, das erzeugte Child statt bool einfach als Rückgabewert zu liefern. Eine andere Lösung wäre es, statt des negativen Rückgabewerte false oder null eine Exception zu werfen, die dann aber wieder jemand behandeln müsste.Eine ganz andere Variante bietet der sogenannte Null-Forgiving-Operator, mit dem man die Nullprüfung effektiv ganz aushebelt. Ähnlich einem Typecast sagt man dem Compiler damit sinngemäß »Vertrau mir, ich weiß schon, was ich mache« und unterdrückt damit eine Warnung:
<span class="hljs-keyword">if</span>( root.CreateNode(<span class="hljs-string">"foobar"</span>, our <span class="hljs-keyword">var</span> child))
root.AddChild( child! ); <span class="hljs-comment">// child never null</span>
<span class="hljs-comment"> </span>
Wem diese Holzhammer-Variante dann doch etwas zu gewagt erscheint, der ist womöglich mit einer Assertion besser bedient, die sich von einer echten Nullprüfung allerdings auch nur noch marginal unterscheidet:
<span class="hljs-keyword">if</span><span class="hljs-comment">( root.CreateNode(„foobar“, our var child)</span>)
{
Debug.Assert<span class="hljs-comment">( child != null)</span>
root.AddChild<span class="hljs-comment">( child)</span>;
}
Die wahrscheinlich beste Lösung für diesen Fall bietet uns aber eine der neuen Annotationen, die das Problem wieder deklarativ lösen kann:
<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">CreateNode</span>(<span class="hljs-params"> ..., [NotNullWhen(<span class="hljs-literal">true</span></span>)] <span class="hljs-keyword">out</span> TreeNode? child)</span>
<span class="hljs-function"> </span>
Durch diese Annotation teilen wir dem Compiler sinngemäß mit: Der Ausgabeparameter ist zwar grundsätzlich nullable, wenn aber diese Methode true liefert, dann ist child garantiert nie null. Da der Compiler nun die von uns beabsichtigte logische Verbindung zwischen Rückgabewert und out-Parameter kennt, entfällt nicht nur die lästige Nullprüfung beim Aufrufer. Der Compiler wird uns nun sogar darauf hinweisen, wenn wir Code schreiben, der gegen diesen Kontrakt verstößt:
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">bool</span> <span class="hljs-title">CreateNode</span>(<span class="hljs-params"> [NotNullWhen(<span class="hljs-literal">true</span></span>)] <span class="hljs-keyword">out</span> TreeNode? child)</span>
{
child = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">// warning because child is null</span>
<span class="hljs-comment">}</span>
Eine andere, gelegentlich anzutreffende Situation ist im folgenden Listing zu finden:
<span class="hljs-keyword">using</span> System.Diagnostics.CodeAnalysis;
<span class="hljs-keyword">class</span> Foo
{
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> content;
<span class="hljs-keyword">public</span> Foo(<span class="hljs-keyword">string</span> <span class="hljs-built_in">text</span>)
{
Initialize(<span class="hljs-built_in">text</span>);
}
<span class="hljs-keyword">public</span> Foo(TextReader reader)
{
Initialize(reader.ReadToEnd());
}
[MemberNotNull(<span class="hljs-string">"content"</span>)]
<span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> Initialize(<span class="hljs-keyword">string</span> <span class="hljs-built_in">text</span>)
{
content = <span class="hljs-built_in">text</span>;
}
<span class="hljs-comment">// ... more code ...</span>
<span class="hljs-comment">}</span>
Eine Klasse hat mehrere Konstruktoren für verschiedene Einsatzfälle. Hat die Klasse nun recht viele Felder, die im Grunde aber immer dieselbe Standardinitialisierung erhalten sollen, die sich aber leider auch nicht gleich in der Variablendeklaration erledigen lässt, kann man diese Initialisierungen in eine Hilfsmethode packen, die hier Initialize() genannt wurde.Problematisch an dieser Konstruktion ist jedoch, dass der Compiler tatsächlich nur den reinen Konstruktor-Code betrachtet und anschließend prompt feststellt, dass dort nicht alle erforderlichen Felder initialisiert wurden. Was aber eben gar nicht stimmt, da diese Arbeit ja in Initialize() erledigt wird.

Auch inkonsistenter Codewird vom Compiler moniert(Bild 2)
Geyer
Hier hilft das Attribut [MemberNotNull(„feld1“,“feld2“)] weiter. Damit teilen wir dem Compiler mit, dass nach dem Verlassen der damit annotierten Methode die angegebenen Felder auf einen Wert ungleich null initialisiert worden sind. Analog wie schon beim vorigen Attribut warnt uns der Compiler auch hier zuverlässig, wenn wir etwa eines der angegebenen Felder vergessen haben und somit gegen den Kontrakt verstoßen würden.Es gibt noch eine ganze Reihe weiterer Attribute, auf deren genaue Erläuterung an dieser Stelle aus Platzgründen verzichtet wird. Es sei stattdessen auf die Dokumentation im MSDN und die exzellente Blogartikel-Serie unter https://endjin.com/blog/2020/05/dotnet-csharp-8-nullable-references-transcending-the-type-system-with-allownull verwiesen.
Einsatz in generischen Klassen
Apropos deklarativ: Einige der neuen syntaktischen Möglichkeiten sind speziell für den Einsatz in generischen Klassen gedacht. So kann etwa die folgende Einschränkung eingesetzt werden, um deklarativ Nullzeiger für beliebige T zu unterbinden:
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FooBar</span><T> <span class="hljs-title">where</span> <span class="hljs-title">T</span> : <span class="hljs-title">notnull</span> </span>
<span class="hljs-class">{ … }</span>
<span class="hljs-class"> </span>
In praktisch relevanten Anwendungen kommt es nicht selten vor, dass einige Quellcodeteile gar nicht manuell geschrieben werden. Um nur ein Beispiel zu nennen. Für WSDL- oder REST-Clients wird beim Import der API-Informationen passender Stub-Code generiert, um komfortabel mit dem serverseitigen Ende kommunizieren zu können. Nicht immer sind diese Generatoren auf die neue Nullzeiger-Semantik angepasst. Gar nicht so selten findet man auch in generiertem Quelltext das <autogenerated>-Tag, welches gleich eine ganze Reihe an Warnungen und Hinweisen pauschal unterdrückt und nebenbei auch die Nullzeigeranalyse deaktiviert. Alternativ tut es auch ein #nullable disable am Beginn der Quellcodedatei.Das allein wäre aber kein Problem, das lauert an gänzlich unerwartetere Stelle. Zeigt man im Editor von Visual Studio mit der Maus auf einen Bezeichner, wird ein Tooltip angezeigt, der unter anderem auch eine Info liefert, ob der Wert der fraglichen Variablen an dieser Stelle nach Stand der Analyse auch null sein kann oder eben nicht null ist (Bild 3).

Die Variableist hier nicht null - oder wurde gar nicht analysiert(Bild 3)
Geyer
Problematisch an dieser Meldung ist, dass sie unter Umständen komplett falsch sein kann. Die positive Formulierung »Variable ist hier nicht null« erscheint nämlich auch dann, wenn der definierende Quelltext von der Analyse ausgeschlossen wurde, etwa via <autogenerated>-Tag. Die korrekte Meldung in so einer Situation wäre eigentlich »Ich weiß es nicht, keine Aussage möglich«.Vertraut der Entwickler nun aber unbesehen auf diesen Hinweis und auf die Tatsache, dass keine sichtbare Wellenlinien-Markierung im Editor erfolgt, kann das in der Tat dazu führen, dass eine eigentlich nötige Nullprüfung im Code weggelassen wird und in der Folge zu einer ansonsten durchaus vermeidbaren NullReferenceException führt.
Fazit
Mit der neuen Nullzeiger-Analyse und dem dazugehörigen Tooling wird dem Entwickler ein gutes Werkzeug an die Hand gegeben. Positiv ist auf jeden Fall das Bemühen um qualitativ besseren Quellcode und der dazu verfolgte konsequent deklarative Ansatz.Das Werkzeug ist wie so oft nur eine Seite der Medaille. Mitarbeit, Problembewußtsein und Aufmerksamkeit des Entwicklers ist ebenso nötig, um das angebotene Werkzeug effektiv zu nutzen und die Warnungen nicht nur als Störfaktor zu begreifen. Lässt man sich aber darauf ein und erwirbt ein paar praktische Erfahrungen im Umgang damit wird es schnell zu einem unverzichtbaren Hilfsmittel.Links zum Thema
<b>◼ <a href="https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references" rel="noopener" target="_blank">Dokumentation zu »Nullable reference types« im MSDN</a><br/></b>