Strategien zur Null-Migration

Tip

Starten eines neuen Projekts? Neue Projekte, die aus .NET 6 oder höher erstellten Vorlagen erstellt wurden, haben bereits <Nullable>enable</Nullable> festgelegt. Sie benötigen keine Migrationsstrategie: Springen Sie zu Nullwerte-Warnungen auflösen.

Verwalten einer vorhandenen Codebasis? Lesen Sie zunächst NULL-Werte zulassende Verweistypen, um Kontexte, Anmerkungen und Nullzustand zu verstehen. In diesem Artikel wird davon ausgegangen, dass Sie mit diesen Konzepten vertraut sind und bereit zum Planen eines Rollouts sind.

Wenn Sie nullable Verweistypen für ein großes Projekt aktivieren, das vor der Einführung nullabler Verweistypen gestartet wurde, erzeugt der Compiler viele Warnungen gleichzeitig. Bei der Migration geht es um die Sequenzierung der Arbeit: Auswählen eines Standardkontexts, Anzeigen von Warnungen nach Datei oder Abschnitt nach Abschnitt und Zusammenwirken <Nullable>enable</Nullable> für das gesamte Projekt. Die richtige Sequenz hängt davon ab, wie aktiv die Codebasis ist und wie viel Risiko Sie in einem einzigen Durchlauf einnehmen können.

Der Endzustand ist in jedem Fall derselbe: das Projekt setzt <Nullable>enable</Nullable> und enthält keine #nullable Präprozessordirektiven.

Auswählen eines Standardkontexts

Der Nullbarkeitskontext weist zwei unabhängige Kennzeichnungen auf: Annotationen (ob ? einen nullable Referenztyp deklariert) und Warnungen (ob der Compiler Diagnosemeldungen ausgibt). Fassen Sie sie als einen einzelnen <Nullable>-Wert zusammen:

Standardwert Annotations Warnungen Am besten geeignet für:
disable (implizit) Aus Aus Stabile Bibliotheken, für die in diesem Durchlauf keine neue Feature-Entwicklung vorgesehen ist.
enable on on Aktive Codebases mit häufigen neuen Dateien. Neuer Code wird bei aktivierter Teilnahme verwendet.
warnings Aus on Zweistufige Migration: zuerst Warnungen beheben, später annotieren.
annotations on Aus Kommentieren Sie die öffentliche API, bevor Sie die internen Warnungen beheben.

Wählen Sie die Strategie aus, die den Zielen für die Projektmigration am besten entspricht:

  • Als Standard deaktivieren. Setzen Sie <Nullable>disable</Nullable> und fügen Sie #nullable enable am Anfang jeder Datei hinzu, während Sie sie migrieren. Vorhandene Dateien bleiben hinsichtlich der Nullwertbarkeit unverändert, bis Sie sie bearbeiten. Diese Option hat die niedrigste Reibung für stabile Bibliotheken, da neue Featurearbeit selten ist.
  • Als Standard aktivieren. Legen Sie <Nullable>enable</Nullable> fest und fügen Sie #nullable disable am Anfang jeder Datei hinzu, die Sie noch nicht migriert haben. Jede neue Datei ist von Anfang an nullfähig, sodass der Migrationsrücklauf nur verkleinert werden kann. Diese Wahl ist besser, wenn die Entwicklung aktiv ist.
  • Warnungen als Standard. Legen Sie <Nullable>warnings</Nullable> fest. Wählen Sie diese Standardeinstellung für eine zweiphasige Migration: Beheben Sie Warnungen, solange noch jeder Referenztyp als nicht annotiert behandelt wird, und aktivieren Sie anschließend Annotationen. Die Aufteilung in zwei Phasen hält den Diff jedes Schritts fokussiert.
  • Anmerkungen als Standard. Legen Sie <Nullable>annotations</Nullable> fest. Annotieren Sie zunächst Ihre öffentliche API (? bei Membern, die null zulassen), bevor Sie Warnungen nachjagen. Der Compiler gibt noch keine Warnungen aus, sodass Sie die API-Oberfläche ohne Ablenkung abgleichen können.

Ihre Projektdatei steuert die globale Standardeinstellung. #nullable Präprozessordirektiven setzen diesen Standardwert für einen Codebereich außer Kraft:

<PropertyGroup>
  <Nullable>enable</Nullable>
</PropertyGroup>

Innerhalb von Quelldateien wählt die Direktive eine Region in oder aus der Null-Einstellung des Projekts aus:

#nullable disable
public static class LegacyHelper
{
    // This file is nullable-oblivious. Reference types use the legacy rules.
    public static string GetGreeting(string name) =>
        name == null ? "hello" : $"hello {name}";
}
#nullable restore

#nullable enable
public static class MigratedHelper
{
    // This file is fully migrated. Reference types are non-nullable by default.
    public static string GetGreeting(string? name) =>
        name is null ? "hello" : $"hello {name}";
}
#nullable restore

Datei für Datei migrieren

Der verlässlichste Weg, ein großes Projekt zu migrieren, besteht darin, Warnungen oder Anmerkungen Datei für Datei zu aktivieren. Das Muster ist unabhängig davon identisch, welche Standardeinstellung Sie auswählen:

  1. Wählen Sie eine Datei aus. Beginnen Sie mit den tiefsten Blatttypen in Ihrem Abhängigkeitsdiagramm, und bewegen Sie sich nach außen. Durch Das Kommentieren eines Typs werden neue Warnungen in den Anrufern verursacht, sodass die Arbeit von unten nach oben die Überarbeitungen minimiert.
  2. Fügen Sie die #nullable Direktive hinzu, die die Datei in das neue Verhalten einwählen soll. Verwenden Sie #nullable enable, wenn Sie beide Flags verwenden möchten. Verwenden Sie #nullable enable warnings nur für Warnungen.
  3. Adressieren Sie die Warnungen in der Datei mithilfe der Techniken in "Auflösen nullabler Warnungen".
  4. Wiederholen Sie den Vorgang für die nächste Datei.
  5. Wenn jede Datei im Projekt eine Direktive hat, entfernen Sie die Direktiven und setzen Sie <Nullable>enable</Nullable> auf Projektebene.

Wenn Ihre Codebasis bereits <Nullable>enable</Nullable> enthält, sind Sie in die entgegengesetzte Richtung unterwegs. Unterdrücken Sie Warnungen in nicht migrierten Dateien, bis Sie bereit sind. Verwenden Sie #nullable disable, um Dateien auszuschließen, und entfernen Sie dann die Unterdrückungen nacheinander.

Migrieren in zwei Phasen

Eine zweistufige Migration trennt die beiden Arten von Arbeiten, die nullfähige Verweistypen umfassen. Sie können die Phasen in beiden Richtungen abfolgen, je nachdem, welche Form der Stabilität ihnen mehr wichtig ist.

Warnungen zuerst, dann Anmerkungen

Warnungen priorisieren, wenn das Beheben latenter System.NullReferenceException Fehler Vorrang hat:

  1. Phase 1: Warnungen beheben. Legen Sie den Standardwert des Projekts auf warnings. Verweistypen bleiben nullable-oblivious, sodass sich das Typsystem noch nicht ändert. Der Compiler gibt an allen Stellen Warnungen aus, an denen Ihr vorhandener Code möglicherweise bereits System.NullReferenceException auslöst. Fügen Sie Nullprüfungen hinzu, strukturieren Sie den Fluss neu, oder wenden Sie Attribute an, bis das Projekt warnungsbereinigt ist. Jeder Fix macht den Produktionscode noch stabiler, auch bevor Anmerkungen vorhanden sind.
  2. Phase 2: Hinzufügen von Anmerkungen. Legen Sie enable als Projektstandard fest. Verweistypen sind jetzt standardmäßig nicht nullfähig, und var lokale Variablen werden nullfähig. Neue Warnungen spiegeln Deklarationen wider, die nicht mit der Verwendung der Variablen übereinstimmen. Fügen Sie ? zu Typen hinzu, die null zulassen sollen. APIs strikter gestalten, die Non-Null-Eingaben erfordern sollten.

Anmerkungen zuerst, dann Warnungen

Beginnen Sie mit Anmerkungen, wenn die Stabilisierung der öffentlichen API-Oberfläche Priorität hat. Diese Abfolge eignet sich für Bibliotheken: Sie können annotierte Signaturen ausliefern, damit Nutzer die richtigen Vertragsdefinitionen sehen, und anschließend die internen Warnungen nach Ihrem eigenen Zeitplan beheben.

  1. Phase 1: Hinzufügen von Anmerkungen. Legen Sie den Standardwert des Projekts auf annotations. Referenztypen werden standardmäßig nicht-nullbar, aber der Compiler gibt keine Warnungen aus, sodass Sie nicht mit unnötigen Meldungen überhäuft werden. Durchlaufen Sie die öffentliche API und fügen Sie ? jedem Member hinzu, der zulässigerweise null zurückgeben oder akzeptieren kann. Schränken Sie die Signaturen ein, die nicht eingeschränkt werden sollten. Da Warnungen deaktiviert sind, können Sie das API-Shape in fokussierten Commits festlegen, ohne die Implementierung gleichzeitig aufzuheben.
  2. Phase 2: Warnungen beheben. Legen Sie den Projektstandard auf enable fest. Die Annotationen, die Sie in Phase 1 hinzugefügt haben, fließen nun in die Nullzustandsanalyse ein, sodass die Warnungen, die der Compiler ausgibt, von Anfang an präziser sind: Jede einzelne weist auf Code hin, dessen Verhalten nicht dem Vertrag entspricht, den Sie bereits veröffentlicht haben. Lösen Sie sie mit den Techniken in "Auflösen nullabler Warnungen" auf.

Auswählen zwischen den Sortierungen

Jede Reihenfolge unterteilt die Phasen in kleinere, leichter überprüfbare Diffs. Eine Phase ändert nur das Verhalten, und die andere ändert nur die Typen. Der Nachteil ist, dass Sie jede Datei zweimal besuchen. Bei ausgereiftem, stabilem Code, bei dem jede Änderung Risiken birgt, lohnen sich die zwei Durchgänge in der Regel. Wählen Sie warnungen zuerst aus, wenn Sie die Ausführung von Code am meisten erschweren möchten. Wählen Sie zuerst Anmerkungen aus, wenn Sie einen stabilen Vertrag veröffentlichen möchten.

Generierter Code ist ausgeschlossen.

Der Compiler behandelt Dateien, die als generiert gekennzeichnet sind, als ob der nullfähige Kontext deaktiviert wurde, unabhängig von der Einstellung des Projekts. Eine Datei wird als generiert betrachtet, wenn eine der folgenden Bedingungen zutrifft:

  • Eine .editorconfig Regel legt generated_code = true für die Datei fest.
  • Der erste Kommentar in der Datei enthält <auto-generated> oder <auto-generated/>.
  • Der Dateiname beginnt mit TemporaryGeneratedFile_.
  • Der Dateiname endet mit .designer.cs, , .generated.cs, .g.csoder .g.i.cs.

Generatoren, die nullwertfähige Ausgabe erzeugen, können sich wieder dafür entscheiden, indem sie #nullable enable am Anfang der generierten Datei ausgeben.

Wenn Sie fertig sind

Nachdem jede Datei am Projektstandard teilnimmt und das <Nullable>enable</Nullable> Element festgelegt ist:

  • Entfernen Sie jede #nullable Direktive in Ihrer Quelle.
  • Entfernen Sie die Initialisierungen null! und default!, die Sie während der Migration nur hinzugefügt haben, um Warnungen zu unterdrücken. Ersetzen Sie sie durch eine ordnungsgemäße Initialisierung, oder machen Sie den Typ zu einem nullable Verweistyp.
  • Prüfen Sie die öffentliche API stichprobenartig. Jedes Member, das null zurückgibt oder akzeptiert, sollte mit ? annotiert werden. Die Anmerkungen sind Teil Ihres Vertrags, sobald das Paket ausgeliefert wird.

Sie befinden sich jetzt im selben Zustand wie neue Projekte: Nullable Referenztypen sind Teil des Typsystems, und alle neuen Warnungen spiegeln eine tatsächliche Nichtübereinstimmung zwischen Deklarationen und Code wider. Verwenden Sie "Auflösen nullabler Warnungen ", um sie beim Auffinden zu beheben.