Was macht null! Aussage bedeuten?


76

Ich habe kürzlich den folgenden Code gesehen:

public class Person
{
    //line 1
    public string FirstName { get; }
    //line 2
    public string LastName { get; } = null!;
    //assign null is possible
    public string? MiddleName {get; } = null;

    public Person(string firstName, string lastName, string middleName)
    {
        FirstName = firstName;
        LastName = lastName;
        MiddleName = middleName;
    }

    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
        MiddleName = null;
    }
}

Grundsätzlich versuche ich, mich mit neuen C # 8-Funktionen zu beschäftigen. Einer von ihnen ist NullableReferenceTypes. Eigentlich gibt es schon viele Artikel und Informationen dazu. ZB ist dieser Artikel ziemlich gut. Aber ich finde keine Informationen zu dieser neuen Aussage. null! Kann mir jemand eine Erklärung dafür geben? Warum muss ich das benutzen? Und was ist der Unterschied zwischen line1und line2?


15
!ist der nullverzeihende Operator , der dem Compiler sagt, dass er, obwohl er es normalerweise nicht zulässt, wegschauen und es trotzdem zulassen sollte, weil wir es besser wissen. null!selbst hat wenig praktischen Nutzen, da es die Nützlichkeit nullbarer Referenztypen so gut wie negiert. Es ist nützlicher, wenn Sie wissen, dass ein Ausdruck nicht sein nullkann, der Compiler jedoch nicht.
Jeroen Mostert

1
@JeroenMostert so etwas wie Kraft? Ich meine, auch wenn es ungewöhnlich ist, lassen Sie uns das erzwingen.
Isxaker

2
Ja, außer es ist mehr als ungewöhnlich - denn stringnach den neuen Regeln ist es kein nullfähiger Referenztyp und sollte es auch niemals sein null. null!Effektives Zuweisen sagt "Ich weiß, das sollte niemals sein null, aber raten Sie mal, ich mache es trotzdem". Es gibt fast kein Programm, in dem dies sinnvoll wäre - der einzige Grund dafür wäre, dass Sie wissen, dass Sie einen Nichtwert zuweisen nullwerden, bevor jemand einen erhalten kann NullReferenceException, und signalisieren möchten, dass Sie nicht vergessen haben, ihn zuzuweisen es. Möglich, aber unwahrscheinlich, also nicht sehr gut als Beispiel.
Jeroen Mostert

Was bedeutet das public string? MiddleName {get; } = null;? Warum Zeichenfolge nullfähig machen? Ist es nicht schon nullfähig? Oder habe ich etwas verpasst?
Imad

1
@JeroenMostert Ich habe die Null gefunden! in Unit-Tests nützlich sein. Nur weil eine Referenz nicht null sein sollte, heißt das nicht unbedingt, dass dies nicht der Fall sein wird. Manchmal ist es daher angebracht, in dieser Situation das richtige Verhalten zu testen.
Jesse

Antworten:


95

Der Schlüssel zum Verständnis der null!Mittel ist das Verständnis des !Bedieners. Möglicherweise haben Sie es zuvor als "nicht" -Operator verwendet. Seit C # 8.0 kann der Operator jedoch auch für einen Typ zur Steuerung der Nullabilty verwendet werden

Was ist der !Operator, wenn er für einen Typ verwendet wird?

Der !Betreiber, wenn sie auf einer Art verwendet wird , wird die genannte Null Versöhnlich Operator [ docs ]. Es wurde in C # 8.0 eingeführt


Technische Erklärung

Typische Verwendung

Unter der Annahme dieser Definition:

class Person
{
  public string? MiddleName;
}

Die Verwendung wäre:

void LogPerson(Person person)
{
    Console.WriteLine(person.MiddleName.Length);  // WARNING: may be null
    Console.WriteLine(person.MiddleName!.Length); // No warning
}

Dieser Operator deaktiviert grundsätzlich die Compiler-Nullprüfungen.

Innenleben

Die Verwendung dieses Operators teilt dem Compiler mit, dass auf etwas zugegriffen werden kann, das null sein kann. Sie drücken die Absicht aus, sich in diesem Fall nicht um die Nullsicherheit zu kümmern.

Es gibt zwei Zustände, in denen sich eine Variable befinden kann - wenn es um Nullsicherheit geht.

  • Nullable - Kann null sein.
  • Nicht nullbar - Kann nicht null sein.

Seit C # 8.0 sind alle Referenztypen standardmäßig nicht nullwertfähig.

Die "Nullbarkeit" kann durch diese 2 neuen Typoperatoren geändert werden:

  • != Von NullablebisNon-Nullable
  • ?= Von Non-NullablebisNullable

Diese Operatoren sind im Grunde genommen Gegenstücke zueinander. Der Compiler verwendet die Informationen, die Sie mit diesen Operatoren definieren, um die Nullsicherheit zu gewährleisten.

? Bedienergebrauch.

  1. Nullable string? x;

    • x ist ein Referenztyp - Standardmäßig also nicht nullwertfähig.
    • Wir wenden den ?Operator an - was ihn nullbar macht.
    • x = null Funktioniert gut.
  2. Nicht nullbar string y;

    • y ist ein Referenztyp - Standardmäßig also nicht nullwertfähig.
    • y = null Erzeugt eine Warnung, da Sie etwas, das nicht null sein soll, einen Nullwert zuweisen.

! Bedienergebrauch.

string x;
string? y = null;
  1. x = y

    • Illegal! - -Warning: "y" may be null
    • Die linke Seite der Zuweisung ist nicht nullbar, die rechte Seite ist nullbar.
  2. x = y!

    • Legal!
    • Die rechte und linke Seite der Zuweisung ist nicht nullwertfähig.
    • Funktioniert seit y!Wendet den !Operator an y, für den er nicht nullwertfähig ist.

WARNUNG Der !Operator deaktiviert die Compilerprüfungen nur auf Typsystemebene. - Zur Laufzeit kann der Wert immer noch null sein.

Dies ist ein Anti-Muster.

Sie sollten versuchen , die Verwendung des !Null-Forgiving-Operators zu vermeiden .

Es gibt gültige Anwendungsfälle (nachstehend ausführlich beschrieben) wie Unit-Tests, bei denen dieser Operator für die Verwendung geeignet ist. In 99% der Fälle sind Sie mit einer alternativen Lösung besser dran. Bitte schlagen Sie nicht Dutzende von !in Ihren Code, nur um die Warnungen zum Schweigen zu bringen. Überlegen Sie, ob Ihre Sache die Verwendung wirklich rechtfertigt.

Verwenden Sie - aber mit Vorsicht. Wenn es keinen konkreten Zweck / Anwendungsfall gibt, ziehen Sie es vor, ihn nicht zu verwenden.

Es negiert die Auswirkungen der Null-Sicherheit, die der Compiler garantiert.

Die Verwendung des !Operators führt zu sehr schwer zu findenden Fehlern. Wenn Sie eine Eigenschaft haben, die als nicht nullbar markiert ist, gehen Sie davon aus, dass Sie sie sicher verwenden können. Aber zur Laufzeit stößt man plötzlich auf einen NullReferenceExceptionund kratzt sich am Kopf. Da ein Wert nach Umgehen der Compiler-Checks mit tatsächlich null wurde !.

Warum existiert dieser Operator dann?

  • In einigen Randfällen kann der Compiler nicht erkennen, dass ein nullbarer Wert tatsächlich nicht nullwertfähig ist.
  • Einfachere Migration älterer Code-Basen.
  • In einigen Fällen ist es Ihnen einfach egal, ob etwas null wird.
  • Wenn Sie mit Unit-Tests arbeiten, möchten Sie möglicherweise das Verhalten von Code überprüfen, wenn a nulldurchkommt.

Beantworten Sie Ihre Frage gezielt.

Was bedeutet null!das?

Es teilt dem Compiler mit, dass dies nullkein nullWert ist. Klingt komisch, nicht wahr?

Es ist das gleiche wie y!im obigen Beispiel. Es sieht nur seltsam aus, da Sie den Operator auf das nullLiteral anwenden . Das Konzept ist jedoch dasselbe.

Herausgreifen, was passiert.

public string LastName { get; } = null!;

Diese Zeile definiert eine nicht nullfähige Klasseneigenschaft mit dem Namen LastNametype string. Da es nicht nullbar ist, können Sie es technisch nicht null zuweisen - offensichtlich.

Aber Sie tun genau das - assign nullzu LastName- durch die Verwendung von !Operator. Weil null!nicht null ist - soweit es den Compiler um Nullsicherheit geht.


1
Eine Zeichenfolge kann null sein. Sie können einen Nullwert wie diesen zuweisen. Str = null; Ich verwende c # 6 .net 4.6.1. Ich denke, Ihre Aussage ist falsch. "Diese Zeile definiert eine nicht nullbare Klasseneigenschaft mit dem Namen LastNameof Typ string. Da sie nicht nullbar ist, können Sie ihr technisch offensichtlich keine Null zuweisen - offensichtlich."
canbax

7
@canbax Nullabilty Checks werden nur von c # 8 und höher unterstützt
Patrick Hollweck

3
@canbax a string could be nullnicht mehr. Ich meine, wenn Sie die Funktion verwenden c# 8und aktivieren NullableReferenceTypes. VS gibt sofort eine Warnung aus, wenn Sie versuchen, null zuzuweisen string. Andererseits können Sie nullable string ( string? s = null) einführen . Keine Warnung in diesem Fall.
Isxaker

3
"Sie sollten versuchen, niemals den! Null-Forgiving-Operator zu verwenden. Er negiert die Auswirkungen der Null-Sicherheit, die der Compiler garantiert." Wie werden Sie Unit-Tests für die Argumentvalidierung schreiben? Das ist meine häufigste Verwendung von! in der Noda-Zeit.
Jon Skeet

1
Ich würde definitiv empfehlen, sie später zu bearbeiten - derzeit könnte sich jeder, der den Null-Vergebungs-Operator in seinen Tests häufig verwendet, angesichts Ihres aktuellen Textes leicht schlecht fühlen, was keinen Hinweis auf Einschränkungen des Ratschlags gibt. Ich denke , „nie“ wahrscheinlich in mehrere Codebasen unerreichbare sein wird - zum Beispiel in Noda Zeit haben wir eine unveränderliche in Parse - Ergebnisse , dass Mittel , die ich nie aussetzen einen Nullwert, aber ich habe noch mit einem Ende , wo der Parse selbst versagt hat . Ich denke, "versuche es zu vermeiden" hat einen positiveren Ton. Nur meine Meinung.
Jon Skeet

14

Wenn die Funktion "nullfähige Referenztypen" aktiviert ist, verfolgt der Compiler, welche Werte in Ihrem Code seiner Meinung nach null sind oder nicht. Es gibt Zeiten, in denen der Compiler möglicherweise nicht über ausreichende Kenntnisse verfügt.

Beispielsweise verwenden Sie möglicherweise ein verzögertes Initialisierungsmuster, bei dem der Konstruktor nicht alle Felder mit tatsächlichen (nicht null) Werten initialisiert, sondern immer eine Initialisierungsmethode aufruft, die garantiert, dass die Felder nicht null sind. In diesem Fall stehen Sie vor einem Kompromiss:

  • Wenn Sie das Feld als nullbar markieren, ist der Compiler zufrieden, aber Sie müssen nicht unbedingt auf null prüfen, wenn Sie das Feld verwenden.
  • Wenn Sie das Feld als nicht nullwertfähig belassen, beschwert sich der Compiler, dass es nicht von den Konstruktoren initialisiert wurde (Sie können dies mit unterdrücken null!), und das Feld kann ohne Nullprüfung verwendet werden.

Beachten Sie, dass Sie mit dem !Unterdrückungsoperator ein gewisses Risiko eingehen. Stellen Sie sich vor, Sie initialisieren nicht alle Felder so konsistent, wie Sie gedacht haben. Dann null!verdeckt die Verwendung von zum Initialisieren eines Feldes die Tatsache, dass a nulleinrutscht. Einige ahnungslose Codes können a empfangen nullund daher fehlschlagen.

Im Allgemeinen haben Sie möglicherweise einige Domänenkenntnisse: "Wenn ich eine bestimmte Methode überprüft habe, weiß ich, dass ein Wert nicht null ist":

if (CheckEverythingIsReady())
{
   // you know that `field` is non-null, but the compiler doesn't. The suppression can help
   UseNonNullValueFromField(this.field!);
}

Auch hier müssen Sie sicher sein, dass Ihr Code unveränderlich ist, um dies zu tun ("Ich weiß es besser").

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.