Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

Hinweis: C# 8.0 nullable reference types -- Achtung vor falscher Sicherheit
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7510
Herkunft: Waidring

Themenstarter:

Hinweis: C# 8.0 nullable reference types -- Achtung vor falscher Sicherheit

beantworten | zitieren | melden

Hallo zusammen,

wie vllt. bekannt ist wird mit C# 8.0 der Kampf dem Billion Dollar MistakeTony Hoare) angesagt. Siehe dazu Design with nullable reference types.

Allerdings darf man sich v.a. bei öffentlichen (public) APIs nicht in falscher Sicherheit wiegen.

Diese Gefahr / Tücke will ich mit nachfolgenden simplen Beispiel demonstrieren.


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string name) => this.Name = name;
    }
}
Für die öffentliche Klasse Person wurde das "nullable feature" explizit aktiviert*, somit könnte man meinen, dass name eben nicht null sein darf.
Das ist für diese Klasse auch korrekt**, andernfalls hätte der Parameter als string? name deklariert werden müssen um null zu erlauben.

Wenn nun ein Benutzer unserer Klasse Person Nullable nicht aktiviert hat, wie im folgenden Code


    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person(null);
            Console.WriteLine(person.Name.Length);
        }
    }
, so kompiliert das Programm ohne Fehler / Warnung, zur Laufzeit gibt es aber eine NullReferenceException, da nirgends validiert wurde ob das Konstruktor-Argument name ungleich Null ist.

Wäre für obigen Code Nullable auch aktiviert worden, so hätte der C#-Compiler eine Warnung / Fehler ausgegeben.
Beim Erstellen eines "public apis" können wir aber nicht davon ausgehen, dass jeder Benutzer Nullable aktiviert hat und somit können wir in die vorhin demonstrierte Falle tappen.

Somit will ich mit diesem Beitrag den Hinweis ausprechen:
Auch mit C# 8.0 Nullable müssen public api auf null validiert werden!.

D.h. eingangs erwähnte Person-Klasse ist auch in Zukunft korrekt zu schreiben:


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string? name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
    }

Hier wurde als Typ string? angegeben, damit verdeutlicht wird dass Verwender dieser Klasse auch null dürfen.

Wäre als Typ string angegeben und der Verwender dieser Klasse hat Nullable sowie TreatWarningsAsErrors aktiviert, so ließe sich der Code mit null gar nicht kompilieren. Dies kann u.U. ein Hindernis sein. Daher bevorzuge ich (momentan***) die Variante mit string?.


* es ist auch eine Aktivierung via Projekteinstellungen möglich, ich empfehle aber die Aktivierung per Compiler-Direktive vorzunehmen, da so beim Lesen vom Code sofort ersichtlich dass "opt-in" für Nullable durchgeführt wurde.

** korrekt im Rahmen der vom Compiler durchgeführten statischen Codeanalyse, die entweder als Warnung od. bei <TreatWarningsAsErrors>true</TreatWarningsAsErrors> (in der csproj) als Fehler ausgegeben wird

*** Nullable ist noch relativ neu, daher kann es sein dass sich meine Präferenz dafür ändert und string (also ohne das Zulassen von null) die bessere Wahl ist

mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
BhaaL
myCSharp.de - Member

Avatar #erP6yAFiewXrJTqrvg6R.jpg


Dabei seit:
Beiträge: 648

beantworten | zitieren | melden

Interessant, ich hatte irgendwo mal gelesen dass der Plan wäre, in dem Fall die Checks/Exceptions/Contracts vom Compiler generiert werden sollen.

Kommt die NRE beim Zugriff auf person.Name.Length oder bereits beim Konstruktor-Aufruf new Person(null)?

Edit: Wies scheint hatte ich hier ein altes Proposal für den dammit Operator im Kopf, dass im Nullable Kontext folgende Syntax erlaubt sein soll(te):

#nullable enable
public static void MyFunc(string name!) { /* ... */ }

// becomes:
public static void MyFunc(string name)
{
    if (name is null) throw new ArgumentNullException(nameof(name));
    /* ... */
}
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von BhaaL am .
private Nachricht | Beiträge des Benutzers
p!lle
myCSharp.de - Member

Avatar #avatar-3556.jpg


Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

Zitat von gfoidl


#nullable enable
Für die öffentliche Klasse Person wurde das "nullable feature" explizit aktiviert*, somit könnte man meinen, dass name eben nicht null sein darf.

Sowas verwirrt mich immer, das Feature heißt "nullable", wenn man es einschaltet aktiviert man aber eigtl. "non-nullable".
Wahrscheinlich verstehe ich wieder irgendwas nicht, aber für mich isses verdreht.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15526
Herkunft: BW

beantworten | zitieren | melden

Es aktiviert nullable reference types. Das Feature ist schon gut so genannt.
private Nachricht | Beiträge des Benutzers
p!lle
myCSharp.de - Member

Avatar #avatar-3556.jpg


Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

Okay, dann verstehe ich es nicht.
Warum darf name dann eben nicht null sein?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15526
Herkunft: BW

beantworten | zitieren | melden

Mit C# 8 kommt die Möglichkeit, dass Referenztypen explizit nicht mehr null sein können.
Tutorial: Express your design intent more clearly with nullable and non-nullable reference types

Früher wusste man bei

public PersonEntity Person {get;set;}
nicht, ob Person nun null ist oder nicht - es musste immer alles geprüft werden.

Mit nullable reference types kann/muss man nun explizit angeben, wenn etwas null sein kann.
Das gilt auch für string.


#nullable enable
public PersonEntity Person {get;set;} // Kann nicht null sein

public PersonEntity? Person {get;set;} // Kann null sein

Das macht am Ende den Code weniger anfällig vor ReferenceExceptions.
private Nachricht | Beiträge des Benutzers
p!lle
myCSharp.de - Member

Avatar #avatar-3556.jpg


Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

Ja, also doch richtig verstanden.
Dann wäre "non-nullable" irgendwie logischer, schließlich kann es ja nicht null sein.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15526
Herkunft: BW

beantworten | zitieren | melden

Nein, denn Du musst nun explizit angeben, dass es nullable ist.
Und man nennt es so, weil es opt-in ist.

non-nullable wäre nicht opt-in.
private Nachricht | Beiträge des Benutzers
p!lle
myCSharp.de - Member

Avatar #avatar-3556.jpg


Dabei seit:
Beiträge: 1050

beantworten | zitieren | melden

Hä?

Du schreibst doch selbst:
Zitat von Abt


#nullable enable
public PersonEntity Person {get;set;} // Kann nicht null sein

D.h. es ist nicht nullable.

EDIT: einfacher und verständlicher wäre wohl ein ? für string und Co. gewesen.
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7510
Herkunft: Waidring

Themenstarter:

beantworten | zitieren | melden

Hallo p!lle,

konkret geht es um die Analogie zu Nullable types (wie z.B. int?), daher passt der Name mit Nullable Reference Types schon.

Mit #nullable enable werden die Nullable Reference Types als optionale Erweiterung der C#-Sprache aktiviert.

Optional ist das Ganze deshalb, damit für betehenden Code keine breaking-change passiert bzw. bestehender Code auch weiterhin kompiliert ohne Warnungen / Fehler zu generieren.

mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7510
Herkunft: Waidring

Themenstarter:

beantworten | zitieren | melden

Hallo BhaaL,
Zitat
Kommt die NRE beim Zugriff auf person.Name.Length oder bereits beim Konstruktor-Aufruf new Person(null)?
Das läuft ab wie bisher, also beim Zugriff auf Length, da Name null ist.
Zitat
Wies scheint hatte ich hier ein altes Proposal ...
Das soll in Zukunft auch kommen. Allerdings weiß ich nicht wie "Zukunft" hier definiert ist.

mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers
CoLo
myCSharp.de - Member



Dabei seit:
Beiträge: 232

beantworten | zitieren | melden

Ich stimme P!lle bei.

#nullable liest sich für mich auf dem ersten Blick als #nullableValuesAreNowAllowed und nicht als
#nullableExplicit oder #nullableReferenceTypes.

Die Benennung #nullableExplicit oder #nullableReferenceTypes hätte ich wesentlich besser empfunden.
private Nachricht | Beiträge des Benutzers