Plattformübergreifende Ziele

Modernes .NET unterstützt mehrere Betriebssysteme und Geräte. Es ist wichtig, dass .NET-Open-Source-Bibliotheken so viele Entwickler wie möglich unterstützen, unabhängig davon, ob sie eine in Azure gehostete ASP.NET-Website oder ein .NET-Spiel in Unity erstellen.

.NET- und .NET Standard-Ziele

.NET- und .NET-Standard-Ziele bieten die beste Möglichkeit, einer .NET-Bibliothek plattformübergreifende Unterstützung hinzuzufügen.

  • .NET Standard ist eine Spezifikation von .NET-APIs, die für alle .NET-Implementierungen verfügbar sind. Wenn Sie Software für .NET Standard entwickeln, können Sie Bibliotheken erstellen, die nur APIs aus einer bestimmten Version von .NET Standard verwenden. Dies bedeutet, dass die Bibliotheken von allen Plattformen verwendet werden können, die diese Version von .NET Standard implementieren.
  • .NET 6-8 sind Implementierungen von .NET. Jede Version ist dabei ein einzelnes Produkt mit einheitlichen Funktionen und APIs, die für Windows-Desktop-Apps und plattformübergreifende Konsolen-Apps, Clouddienste und Websites verwendet werden können.

Einen Vergleich zwischen .NET und .NET Standard finden Sie unter .NET 5 und .NET Standard.

.NET Standard

Wenn Ihr Projekt auf .NET oder .NET Standard ausgerichtet ist und erfolgreich kompiliert wird, garantiert es nicht, dass die Bibliothek auf allen Plattformen erfolgreich ausgeführt wird:

  • Bei plattformspezifischen APIs tritt auf anderen Plattformen ein Fehler auf. Beispielsweise wird Microsoft.Win32.Registry unter Windows erfolgreich ausgeführt, gibt jedoch unter anderen Betriebssystemen PlatformNotSupportedException aus.
  • APIs können sich unterschiedlich verhalten. Beispielsweise haben Reflektions-APIs unterschiedliche Leistungsmerkmale, wenn eine Anwendung eine Ahead-of-time-Kompilierung auf iOS oder UWP verwendet.

Tipp

Das .NET-Team stellt ein Analysetool für die Plattformkompatibilität an, mit dem Sie mögliche Probleme erkennen können.

✔️ Beginnen Sie, indem Sie ein netstandard2.0-Ziel einschließen.

Die meisten Bibliotheken für allgemeine Zwecke erfordern außerhalb von .NET Standard 2.0 keine APIs. .NET Standard 2.0 wird von allen modernen Plattformen unterstützt und ist der empfohlene Weg, um mehrere Plattformen mit einem Ziel zu unterstützen. Wenn Sie .NET Framework nicht unterstützen müssen, können Sie auch .NET Standard 2.1 verwenden.

✔️ VERWENDEN Sie ein net6.0-Ziel (oder eine höhere Version), wenn Sie neue APIs benötigen, die in einer modernen .NET-Umgebung eingeführt wurden.

Apps ab .NET 6 können ein netstandard2.0-Ziel verwenden, sodass net6.0 nicht benötigt wird. Sie sollten explizit auf net6.0, net7.0 oder net8.0 abzielen, wenn Sie neuere .NET-APIs verwenden möchten.

❌ Vermeiden Sie es, ein netstandard1.x-Ziel einzuschließen.

.NET Standard 1.x wird als ein granularer Satz von NuGet-Paketen verteilt, der ein großes Abhängigkeitsdiagramm erstellt und dazu führt, dass Entwickler beim Erstellen viele Pakete herunterladen. Moderne .NET-Implementierungen unterstützen .NET Standard 2.0. Sie sollten.NET Standard 1.x nur dann als Ziel verwenden, wenn Sie speziell auf eine ältere Plattform abzielen müssen.

✔️ Schließen Sie ein netstandard2.0-Ziel ein, wenn Sie ein netstandard1.x-Ziel benötigen.

Alle Plattformen, die .NET Standard 2.0 unterstützen, verwenden das netstandard2.0-Ziel und profitieren von einem kleineren Paketdiagramm, während ältere Plattformen noch funktionieren und auf die Verwendung des netstandard1.x-Ziels zurückgreifen.

❌ Fügen Sie kein .NET Standard-Ziel ein, wenn die Bibliothek auf einem plattformspezifischen App-Modell basiert.

Beispielsweise hängt eine Bibliothek eines UWP-Steuerelemente-Toolkit von einem Appmodell ab, das nur in UWP verfügbar ist. App-Modellspezifische APIs sind in .NET Standard nicht verfügbar.

Festlegung von Zielversionen

Manchmal müssen über Ihre Bibliotheken auf Framework-spezifische APIs zugreifen. Der beste Weg, Framework-spezifische APIs aufzurufen, ist die Verwendung der Festlegung von Zielversionen, die Ihr Projekt für viele .NET-Zielframeworks statt nur für eine erstellt.

Um Ihre Kunden davor zu schützen, Bibliotheken für einzelne Frameworks erstellen zu müssen, sollten Sie eine .NET Standard-Ausgabe sowie eine oder mehrere Framework-spezifische Ausgaben anstreben. Bei der Festlegung von Zielversionen werden alle Assemblys in einem einzigen NuGet-Paket verpackt. Die Benutzer können dann auf das gleiche Paket zurückgreifen, und NuGet wählt die passende Implementierung aus. Ihre .NET Standard-Bibliothek dient als Fallbackbibliothek, die überall verwendet wird, mit Ausnahme der Fälle, in denen Ihr NuGet-Paket eine Framework-spezifische Implementierung bietet. Die Festlegung von Zielversionen ermöglicht es Ihnen, die bedingte Kompilierung in Ihrem Code zu verwenden und Framework-spezifische APIs aufzurufen.

NuGet package with multiple assemblies

✔️ Erwägen Sie das Targeting von .NET-Implementierungen zusätzlich zu .NET Standard.

Das Targeting von .NET-Implementierungen ermöglichen es Ihnen, plattformspezifische APIs aufzurufen, die sich außerhalb von .NET-Standard befinden.

Dabei muss weiterhin der Support für .NET Standard gewährleistet sein. Lösen Sie sich stattdessen von der Implementierung und bieten Sie Funktions-APIs. Auf diese Weise kann Ihre Bibliothek überall verwendet werden und unterstützt die Laufzeithervorhebung von Funktionen.

public static class GpsLocation
{
    // This project uses multi-targeting to expose device-specific APIs to .NET Standard.
    public static async Task<(double latitude, double longitude)> GetCoordinatesAsync()
    {
#if NET462
        return CallDotNetFrameworkApi();
#elif WINDOWS_UWP
        return CallUwpApi();
#else
        throw new PlatformNotSupportedException();
#endif
    }

    // Allows callers to check without having to catch PlatformNotSupportedException
    // or replicating the OS check.
    public static bool IsSupported
    {
        get
        {
#if NET462 || WINDOWS_UWP
            return true;
#else
            return false;
#endif
        }
    }
}

✔️ ERWÄGEN Sie die Festlegung von mehreren Zielversionen, auch wenn der Quellcode für alle Ziele identisch ist, wenn ihr Projekt Bibliotheks- oder Paketabhängigkeiten aufweist.

Die abhängigen (direkten oder nachgeschalteten) Pakete Ihres Projekts können möglicherweise dieselben Code-APIs verwenden, während sie in verschiedenen Versionen der abhängigen Assembly pro Zielframework umschlossen sind. Durch das Hinzufügen bestimmter Ziele wird sichergestellt, dass Ihre Consumer ihre Assemblybindungsumleitungen nicht hinzufügen oder aktualisieren müssen.

❌ VERMEIDEN Sie das Festlegen von mehreren Zielversionen sowie das Verwenden von .NET Standard als Ziel, wenn Ihr Quellcode für alle Ziele identisch ist und Ihr Projekt keine Bibliotheks- oder Paketabhängigkeiten aufweist.

Die .NET Standard-Assembly wird von NuGet automatisch verwendet. Das Targeting einzelner .NET-Implementierungen erhöht die *.nupkg-Größe, ohne dass sich daraus Vorteil ergeben.

✔️ Erwägen Sie, ein Ziel für net462 hinzuzufügen, wenn Sie ein netstandard2.0-Ziel anbieten.

Bei der Verwendung von .NET Standard 2.0 aus .NET Framework gibt es einige Probleme, die in .NET Framework 4.7.2 behoben wurden. Sie können das Erlebnis für Entwickler, die noch mit .NET Framework 4.6.2 bis 4.7.1 arbeiten, verbessern, indem Sie ihnen eine Binärdatei anbieten, die für .NET Framework 4.6.2 erstellt wurde.

✔️ DO Verteilen Sie Ihre Bibliothek mithilfe eines NuGet-Pakets.

NuGet wählt das beste Ziel für den Entwickler aus und schützt ihn davor, die passende Implementierung auswählen zu müssen.

✔️ Verwenden Sie bei der Festlegung von Zielversionen die TargetFrameworks-Eigenschaft der Projektdatei.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!-- This project will output netstandard2.0 and net462 assemblies -->
    <TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
  </PropertyGroup>
</Project>

✔ Erwägen Sie bei der Festlegung von Zielversionen für UWP und Xamarin die Verwendung von MSBuild.Sdk.Extras, da es Ihre Projektdatei stark vereinfacht.

❌ VERMEIDEN Sie das Ändern des Assemblynamens oder die Verwendung unterschiedlicher Assemblynamen für jede TFM, die Ihre Bibliothek kompiliert. Aufgrund von Abhängigkeiten zwischen Bibliotheken kann die Multiadressierung mit unterschiedlichen Assemblynamen pro TFM Paketverbraucher unterbrechen. Eine Assembly sollte für alle TFMs denselben Namen haben.

Ältere Ziele

.NET unterstützt die Festlegung von Zielversionen des .NET Framework, die seit langem nicht mehr unterstützt werden, sowie Plattformen, die nur noch selten verwendet werden. Es ist zwar sinnvoll, Ihre Bibliothek mit so vielen Zielen wie möglich arbeiten zu lassen, aber die Notwendigkeit, fehlende APIs zu umgehen, kann zu einem erheblichen Aufwand führen. Wir glauben, dass bestimmte Frameworks angesichts ihrer Reichweite und Grenzen nicht mehr als Ziel genutzt werden sollten.

❌ Schließen Sie keine portable Klassenbibliothek (PCL) als Ziel ein. Beispiel: portable-net45+win8+wpa81+wp8.

.NET Standard ist die moderne Möglichkeit, um plattformübergreifende .NET-Bibliotheken zu unterstützen und ist der Ersatz für PCLs.

❌ Schließen Sie keine Ziele für .NET-Plattformen ein, die nicht mehr unterstützt werden. Platzhalter in einer derartigen Schreibweise sind z.B. SL4 und WP.