Laden...

Mögliche Nullverweiszuweisung bzw. Dereferenzierung eines möglichen Nullverweises

Letzter Beitrag vor einem Jahr 11 Posts 560 Views
Mögliche Nullverweiszuweisung bzw. Dereferenzierung eines möglichen Nullverweises

Hallo,

ich habe folgende Frage. Visual Studio 2022 meckert in der unten stehenden Zeile gleich zwei Male:


ds.Tables["Artikel"].PrimaryKey = new DataColumn[1] { ArtikelColumns["NUMMER"] };

Bei "ds.Tables["Artikel"].PrimaryKey" meldet VS: Mögliche Nullverweiszuweisung

und bei "ArtikelColumns["NUMMER"]": Dereferenzierung eines möglichen Nullverweises

Die grün unterwellten Stellen habe ich hier unterstrichen.

Ich weiß aber, dass dies nicht vorkommen kann, daher schalte ich diese Warnungen für die Zeile ab:


#pragma warning disable CS8601, CS8602    // Mögliche Nullverweiszuweisung bzw. Dereferenzierung eines möglichen Nullverweises
ds.Tables["Artikel"].PrimaryKey = new DataColumn[1] { ArtikelColumns["NUMMER"] };
#pragma warning restore CS8602, CS8601

Nicht schön, aber zunächst egal.

Nun es ist so, dass an vielen Stellen VS Warnungen unterbindet, wenn man davor eine Prüfung macht. Daher habe ich testhalber folgenden (hässlichen) Code getestet:


if (ds != null && ds.Tables != null || ds?.Tables?["Artikel"] != null && ArtikelColumns["NUMMER"] != null)
{
	ds.Tables["Artikel"].PrimaryKey = new DataColumn[1] { ArtikelColumns["NUMMER"] };
}

Es hat sich aber nichts geändert.

Versagt hier IntelliSense oder mache ich grundsätzlich was falsch und ich merke es nur nicht?

Liebe Grüße

René

René

Deine Prüfung macht auch wenig Sinn, wenn ds != null geht er schon rein. ds.Tables["Artikel"] kann ja immer noch fehlen.

Deine Prüfung macht auch wenig Sinn, wenn ds != null geht er schon rein. ds.Tables["Artikel"] kann ja immer noch fehlen.

Ja, hast du Recht, sorry. Ich habe nun die Abfrage über die Zwischenablage kopiert, um keinen doofen Fehler mehr zu machen:


if (ds == null || ds.Tables == null || ds.Tables["Artikel"] == null || ArtikelColumns["NUMMER"] == null)
{
	ds.Tables["Artikel"].PrimaryKey = new DataColumn[1] { ArtikelColumns["NUMMER"] };
}

Es ändert sich jedoch nichts (siehe Bild in der Dateianlage).

LG

René

René

Hallo ihr beiden,

jetzt ist die Abfrage ja genau falsch herum, es müsste doch so aussehen:


if (ds != null && ds.Tables != null && ds.Tables["Artikel"] != null && ArtikelColumns["NUMMER"] != null)

Aber müsste zumindestens für die erste Warnung nicht einfach der ?.-Operator reichen?


ds?.Tables["Artikel"]?.PrimaryKey = ...

Bei der zweiten kommt es darauf an, wie ArtikelColumns definiert und zugewiesen ist.
Ich denke, gemeint ist, daß diese Variable selbst null sein kann, daher entweder auch auf ArtikelColumns != null prüfen oder


new DataColumn[1] { ArtikelColumns?["NUMMER"]};

Dann kann jedoch danach null als Wert in dem DataColumn-Array stehen.

jetzt ist die Abfrage ja genau falsch herum, es müsste doch so aussehen:

  
if (ds != null && ds.Tables != null && ds.Tables["Artikel"] != null && ArtikelColumns["NUMMER"] != null)  
  

Um Gottes Willen! Ich freue mich auf Weihnachten – ein paar Feiertage werden mir gut tun.

Dennoch ändert sich an den zwei Warnungen nichts. Es ist auch nicht ganz wichtig, denn ich weiß schon (vorher geprüft), dass alles korrekt ist. Daher:


#pragma warning disable CS8601, CS8602     // Mögliche Nullverweiszuweisung bzw. Dereferenzierung eines möglichen Nullverweises                                                                                          
			ds.Tables["Artikel"].PrimaryKey	= new DataColumn[1] { ArtikelColumns["NUMMER"] };
#pragma warning restore CS8602, CS8601

Dennoch würde ich gerne wissen, warum VS das als Warnung einstuft.

Aber müsste zumindestens für die erste Warnung nicht einfach der ?.-Operator reichen?

  
ds?.Tables["Artikel"]?.PrimaryKey = ...  
  

Geht nicht: "Die linke Seite einer Zuweisung muss eine Variable, eine Eigenschaft oder ein Indexer sein"

Bei der zweiten kommt es darauf an, wie ArtikelColumns definiert und zugewiesen ist.
Ich denke, gemeint ist, daß diese Variable selbst null sein kann, daher entweder auch auf ArtikelColumns != null prüfen oder

  
new DataColumn[1] { ArtikelColumns?["NUMMER"]};  
  

Dann kann jedoch danach null als Wert in dem DataColumn-Array stehen.

Auch schon getestet, aber es ändert sich weiterhin nichts.

Danke und lG

René

René

Stimmt, der zweite ?.-Operator gibt (leider) diese Fehlermeldung. Also doch


var table = ds?.Tables["Artikel"];
if (table != null)
    table.PrimaryKey = ...;

Und auch bei der zusätzlichen Abfrage


if (ArtikelColumns != null)
  ... = new DataColumn[1] { ArtikelColumns["NUMMER"] };

kommt immer noch die Warnung CS8602?
Dann wird es daran liegen, daß diese DataColumn eben null sein kann - also auch hier


var dataColumn = ArtikelColumns["NUMMER"];
if (dataColumn != null)
    ... = new DataColumn[1] { dataColumn };

Wenn du dir sicher bist, daß zur Laufzeit niemals null herauskommt (bzw. eben eine mögliche NullReferenceException in Kauf nimmst), dann kannst du die Warnungen abschalten, anstatt den komplexeren Code zu schreiben.
Der Compiler kann ja nicht wissen, ob die Einträge "Artikel" bzw. "NUMMER" wirklich zur Laufzeit existieren, daher wohl die Warnungen.

Und zwischen den Aufrufen könnte sich ja auch der Wert geändert.
Bei der Prüfung nicht null, aber dann bei der Zuweisung.

Hier bietet sich ggf. die Verwendung des ! (null-forgiving) operator (C# reference) an,
wenn sichergestellt ist das der Wert nicht null ist.

null-forgiving ist eigentlich wirklich nur für konzeptionelle Dinge gedacht, bei denen man (noch) kein anderen Weg nutzen kann.
Beispiele sind da Linq-Queries, Modell Conventions (zB EF Core)....

Hallo Th69,

in jeder erdenklichen Kombination werden beide Warnungen ausgegeben. Wie gesagt, an dieser Stelle nicht schlimm, denn ich weiß, dass alles OK ist. Daher habe ich nur für die betroffene Zeile diese Compilerwarnungen deaktiviert:


#pragma warning disable CS8601, CS8602     // Mögliche Nullverweiszuweisung bzw. Dereferenzierung eines möglichen Nullverweises
			ds.Tables["Artikel"].PrimaryKey	= new DataColumn[1] { ArtikelColumns["NUMMER"] };
#pragma warning restore CS8602, CS8601

Nicht schlimm, mich hatte nur interessiert, warum der Compiler an dieser Stelle so beharrlich ist.

Danke und Grüße

René

René

Hier bietet sich ggf. die Verwendung des
>
an,
wenn sichergestellt ist das der Wert nicht null ist.

Danke! Wie sagte eine berühmte Fernsehdame? "Hier werden Sie geholfen!"

Du kannst lachen, aber ich kannte diesen Postfixoperator nicht. Mit dem kann ich an der besagten Stelle in der Tat, die Compilerwarnung abschalten:


ds.Tables["Artikel"]!.PrimaryKey = new DataColumn[1] { ArtikelColumns["NUMMER"]! }

Ob das aber sicher und übersichtlicher als die zwei hässlichen #pragma-Zeilen ist, bin ich mir noch nicht ganz im Klaren – wahrscheinlich aber ja, denn in beiden Fällen würde das Programm genau an dieser Stelle eine Ausnahme werfen, wenn meine Behauptung der vollkommenen Korrektheit nicht zutreffend wäre.

@Abt
Wenn ich Microsoft https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving?WT.mc_id=DT-MVP-5001507 richtig verstehe, kann ich durchaus diesen Postfixoperator für meinen Zweck nutzen:

You can also use the null-forgiving operator when you definitely know that an expression can't be null but the compiler doesn't manage to recognize that. In the following example, if the IsValid method returns true, its argument isn't null and you can safely dereference it:

Sehr interessant, denn ich habe durch eure Hilfe was neues gelernt.

Vielen Dank und einen erfolgreichen Start in die neue Woche!

LG

René

René

Dein Fall ist anders als der aus den Docs, damit auch der Code - ergo hinkt der Vergleich.

Im Endeffekt hast Du einen Folgefehler im Code, den viele spüren, wenn sie plötzlich mit Null Warnings konfrontiert werden.
In Deinem Code prüfst Du eine Referenz, ob sie existiert: zB


ds.Tables["Artikel"] != null

und an weiter Stelle machst Du exakt das gleiche, nur dies mal nicht als Vergleich sondern als Zuweisung


ds.Tables["Artikel"].PrimaryKey = .... // das hier gibt eine Compiler Warnung

Das war a) schon immer ineffizient, weil Du hier mehrfach das Zeug von Tables über den Index holen musst und b) ist zusätzlich jetzt halt doof wegen Compiler Warnings, die zurecht erfolgen.
Für a) gab es schon immer die Lösung, dass man eben Dont Repeat Yourself beachtet und solch eine Abfrage in eine Variable auslagert


DataTable? dt = ds.Tables["Artikel"];
if(dt is not null)
{
    dt.PrimaryKey = .... // das hier gibt kein Compiler Warning, da der Compiler erkennt, dass dt hier auf null geprüft wurde
}

Das ist im Vergleich viel sauber und viel effizienter. Das kostet tatsächlich im realen Leben auch weniger CPU-Leistung.
Hättest die Grundregel von DRY befolgt, gäbe es nebenbei diesen Effekt gar nicht 😉

Mit Pattern Matching ist das mittlerweile auch einfacher / aka "schicker" umsetzen


if (ds?.Tables is DataTableCollection tables && tables["Artikel"] is DataTable articleTable)
{
   articleTable.PrimaryKey = new DataColumn[1] { .. gleiches Prinzip für Data Column };
}

Null Forgiving hat wirklich einen anderen Use Case als schmutzigen Code zu verstecken 😉