Analysetool für die Plattformkompatibilität

Sie haben wahrscheinlich bereits von .NET als einer einzigen, einheitlichen Plattform für die Entwicklung beliebiger Anwendungstypen gehört. Das .NET 5 SDK enthält ASP.NET Core, Entity Framework Core, WinForms, WPF, Xamarin und ML.NET. Unterstützung für weitere Plattformen wird im Laufe der Zeit hinzugefügt. Es ist das Ziel von .NET 5, dass Sie sich keine Gedanken um die verschiedenen .NET-Typen machen müssen, ohne dass das zugrunde liegende Betriebssystem jedoch vollständig abstrahiert wird. Sie sind weiterhin in der Lage, plattformspezifische APIs (z. B. P/Invokes, WinRT oder die Xamarin-Bindungen für iOS und Android) aufzurufen.

Durch die Verwendung plattformspezifischer APIs für eine Komponente funktioniert der Code jedoch nicht mehr auf allen Plattformen. Es fehlte eine Methode, die dies zur Entwurfszeit erkennt, damit Entwickler eine Diagnose erhalten, wenn sie versehentlich plattformspezifische APIs verwenden. Aus diesem Grund führt .NET 5 das Analysetool für die Plattformkompatibilität sowie ergänzende APIs ein, damit Entwickler*innen plattformspezifische APIs erkennen und verwenden können, wenn es sinnvoll ist.

Zu den neuen APIs zählen:

  • SupportedOSPlatformAttribute zum Kommentieren von APIs als plattformspezifisch und UnsupportedOSPlatformAttribute, um APIs als für ein bestimmtes Betriebssystem nicht unterstützt zu annotieren. Diese Attribute können optional die Versionsnummer enthalten und wurden bereits auf einige plattformspezifische APIs in den .NET-Kernbibliotheken angewendet.
  • Die statischen Methoden Is<Platform>() und Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) in der Klasse System.OperatingSystem zum sicheren Aufrufen von plattformspezifischen APIs. Mit OperatingSystem.IsWindows() kann beispielsweise ein Aufruf einer Windows-spezifischen API und mit OperatingSystem.IsWindowsVersionAtLeast() ein versionierter Windows-spezifischer API-Aufruf geschützt werden. In diesen Beispielen wird veranschaulicht, wie mithilfe dieser Methoden plattformspezifische API-Verweise geschützt werden können.

Voraussetzungen

Bei dem Analysetool für die Plattformkompatibilität handelt es sich um eines der Codequalitätsanalysetools von Roslyn. Ab .NET 5 sind diese Analysetools im .NET SDK enthalten. Das Analysetool für die Plattformkompatibilität ist standardmäßig nur für Projekte aktiviert, die für net5.0 oder höher entwickelt werden. Sie können es jedoch auch für Projekte aktivieren, die auf andere Frameworks abzielen.

Bestimmen der Plattformabhängigkeit mithilfe des Analysetools

  • Bei einer nicht attributierten API wird davon ausgegangen, dass sie auf allen Betriebssystemplattformen funktioniert.

  • Eine API, die mit [SupportedOSPlatform("platform")] gekennzeichnet ist, gilt als nur für die angegebene Plattform portierbar sowie für alle Plattformen, von denen sie eine Teilmenge ist.

    • Das Attribut kann mehrmals angewendet werden, um Unterstützung für mehrere Plattformen anzugeben (z. B. [SupportedOSPlatform("windows"), SupportedOSPlatform("Android29.0")]).
    • Wenn die Plattform eine Teilmenge einer anderen Plattform bildet, impliziert das Attribut, dass die übergeordnete Plattform ebenfalls unterstützt wird. [SupportedOSPlatform("iOS")] bedeutet beispielsweise, dass die API auf iOS und auch auf der übergeordneten Plattform MacCatalyst unterstützt wird.
    • Das Analysetool generiert eine Warnung, wenn ohne einen ordnungsgemäßen Plattformkontext auf plattformspezifische APIs verwiesen wird:
      • Warnung, wenn das Projekt nicht auf die unterstützte Plattform ausgerichtet ist (z. B. bei einem Windows-spezifischen API-Aufruf aus einem Projekt, das auf iOS ausgerichtet ist <TargetFramework>net5.0-ios14.0</TargetFramework>).
      • Warnung, wenn das plattformübergreifende Projekt plattformspezifische APIs aufruft (z. B. bei einem Windows-spezifischen API-Aufruf aus einem plattformübergreifenden TFM <TargetFramework>net5.0</TargetFramework>).
      • Keine Warnung, wenn innerhalb eines Projekts, das auf eine der angegebenen Plattformen ausgerichtet ist, auf die plattformspezifische API verwiesen wird (z. B. bei einem Windows-spezifischen API-Aufruf aus einem Projekt, das auf Windows ausgerichtet ist <TargetFramework>net5.0-windows</TargetFramework> und für das die Generierung der Datei AssemblyInfo.cs aktiviert ist).
      • Keine Warnung, wenn der plattformspezifische API-Aufruf durch entsprechende Plattformprüfmethoden geschützt wird (z. B. bei einem Windows-spezifischen API-Aufruf, der von OperatingSystem.IsWindows() geschützt wird).
      • Keine Warnung, wenn aus dem gleichen plattformspezifischen Kontext auf die plattformspezifische API verwiesen wird (Aufrufort auch attributiert mit [SupportedOSPlatform("platform"))
  • Eine API, die mit [UnsupportedOSPlatform("platform")] gekennzeichnet ist, gilt für die angegebene Plattform sowie für alle Plattformen, von denen sie eine Teilmenge ist, als nicht unterstützt. Auf allen anderen Plattformen wird sie dagegen unterstützt.

    • Das Attribut kann mehrmals für verschiedene Plattformen angewendet werden, z. B. [UnsupportedOSPlatform("iOS"), UnsupportedOSPlatform("Android29.0")].
    • Wenn die Plattform eine Teilmenge einer anderen Plattform ist, impliziert das Attribut, dass die übergeordnete Plattform ebenfalls nicht unterstützt wird. [UnsupportedOSPlatform("iOS")] bedeutet beispielsweise, dass die API auf iOS und auch auf der übergeordneten Plattform MacCatalyst nicht unterstützt wird.
    • Das Analysetool generiert nur dann eine Warnung, wenn platform für den gesamten Aufrufort gilt:
      • Warnung, wenn das Projekt auf die Plattform ausgerichtet ist, die laut Attribut nicht unterstützt wird (z. B. bei einer mit [UnsupportedOSPlatform("windows")] attributierten API, wenn der Aufrufort auf <TargetFramework>net5.0-windows</TargetFramework> abzielt)

      • Warnung, wenn das Projekt auf mehrere Frameworks abzielt und platform in der Standardelementgruppe MSBuild <SupportedPlatform> enthalten ist oder platform manuell in die Elementgruppe MSBuild<SupportedPlatform> eingefügt wird:

        <ItemGroup>
            <SupportedPlatform Include="platform" />
        </ItemGroup>
        
      • Keine Warnung, wenn Sie eine App erstellen, die nicht auf die nicht unterstützte Plattform oder mehrere Zielplattform ausgerichtet ist, und die Plattform nicht in der Standardelementgruppe MSBuild <SupportedPlatform> enthalten ist

  • Beide Attribute können mit oder ohne Versionsnummern als Teil des Plattformnamens instanziiert werden. Versionsnummern weisen das Format major.minor[.build[.revision]] auf. major.minor ist erforderlich, die Teile build und revision sind optional. „Windows6.1“ gibt z. B. die Windows-Version 6.1 an, während „Windows“ wie „Windows 0.0“ interpretiert wird.

Weitere Informationen finden Sie unter Beispiele für die Funktionsweise von Attributen und ihre Diagnosen.

Erkennen von TFM-Zielplattformen durch das Analysetool

Das Analyseprogramm überprüft keine Zielplattformen des Zielframeworkmonikers (TFM) aus MSBuild-Eigenschaften, z. B. <TargetFramework> oder <TargetFrameworks>. Wenn das TFM über eine Zielplattform verfügt, fügt MSBuild ein SupportedOSPlatform-Attribut mit dem Zielplattformnamen in die Datei AssemblyInfo.cs ein, die vom Analysetool verwendet wird. Wenn der TFM beispielsweise net5.0-windows10.0.19041 ist, fügt MSBuild das [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")]-Attribut in die Datei AssemblyInfo.cs ein, und die gesamte Assembly wird als nur Windows betrachtet. Daher würde der Aufruf von reinen Windows-APIs mit Version 7.0 oder älter keine Warnungen im Projekt verursachen.

Hinweis

Wenn die AssemblyInfo.cs-Dateigenerierung für das Projekt deaktiviert ist (d. h., die <GenerateAssemblyInfo>-Eigenschaft ist auf false festgelegt), kann das erforderliche SupportedOSPlatform-Attribut Assemblyebene nicht von MSBuild hinzugefügt werden. In diesem Fall können Warnungen für eine plattformspezifische API-Nutzung angezeigt werden, auch wenn Sie diese Plattform als Ziel verwenden. Aktivieren Sie die AssemblyInfo.cs-Dateigenerierung, oder fügen Sie das Attribut manuell in Ihrem Projekt hinzu, um die Warnungen zu beheben.

Plattformeinbindung

In .NET 6 wird das Konzept der Plattformeinbindung eingeführt, bei dem eine Plattform eine Teilmenge einer anderen Plattform bilden kann. Ein Attribut für die Teilmengenplattform impliziert die gleiche Unterstützung (oder das Fehlen von Unterstützung) für die übergeordnete Plattform. Wenn eine Plattformprüfmethode im Typ OperatingSystem über ein SupportedOSPlatformGuard("supersetPlatform")]-Attribut verfügt, gilt supersetPlatform als übergeordnete Plattform der Betriebssystemplattform, die die Methode überprüft.

Angenommen, die Methode OperatingSystem.IsIOS() verfügt über das Attribut [SupportedOSPlatformGuard("MacCatalyst")]. Dann gelten die folgenden Aussagen:

  • Die Methoden OperatingSystem.IsIOS() und OperatingSystem.IsIOSVersionAtLeast überprüfen nicht nur die iOS-Plattform, sondern auch die MacCatalyst-Plattform.
  • [SupportedOSPlatform("iOS")] bedeutet, dass die API auf iOS und auch auf der übergeordneten Plattform MacCatalyst unterstützt wird. Mit dem Attribut [UnsupportedOSPlatform("MacCatalyst")] können Sie diese implizite Unterstützung ausschließen.
  • [UnsupportedOSPlatform("iOS") bedeutet, dass die API auf iOS und MacCatalyst nicht unterstützt wird. Mit dem Attribut [SupportedOSPlatform("MacCatalyst")] können Sie das implizite Fehlen der Unterstützung ausschließen.

In der folgenden Abdeckungstabelle bedeutet ✔️, dass die Plattform unterstützt wird und ❌, dass die Plattform nicht unterstützt wird.

Plattform SupportedOSPlatform(subset) SupportedOSPlatform(superset) UnsupportedOSPlatform(subset) UnsupportedOSPlatform(superset)
Teilmengenplattform ✔️ ✔️
Übergeordnete Plattform ✔️ ✔️ ✔️ ✔️

Tipp

Diese Regeln gelten auch für die Attribute SupportedOSPlatformGuard und UnsupportedOSPlatformGuard.

Der folgende Codeausschnitt zeigt, wie Sie Attribute kombinieren können, um den korrekten Unterstützungsumfang festzulegen.

  // MacCatalyst is a superset of iOS therefore supported on iOS and MacCatalyst  
  [SupportedOSPlatform("iOS")]
  public void ApiOnlySupportedOnIOSAndMacCatalyst() { }

  // Does not imply iOS, only supported on MacCatalyst
  [SupportedOSPlatform("MacCatalyst")]
  public void ApiOnlySupportedOnMacCatalyst() { }

  [SupportedOSPlatform("iOS")] // Supported on iOS and MacCatalyst  
  [UnsupportedOSPlatform("MacCatalyst")] // Removes implied MacCatalyst support
  public void ApiOnlySupportedOnIos() { }

  // Unsupported on iOS and MacCatalyst  
  [UnsupportedOSPlatform("iOS")]
  public void ApiUnsupportedOnIOSAndMacCatalyst();

  // Does not imply iOS, only unsupported on MacCatalyst
  [UnsupportedOSPlatform("MacCatalyst")]
  public void ApiUnsupportedOnMacCatalyst() { }

  [UnsupportedOSPlatform("iOS")] // Unsupported on iOS and MacCatalyst  
  [SupportedOSPlatform("MacCatalyst")] // Removes implied MacCatalyst unsupportedness
  public void ApiUnsupportedOnIos() { }

Komplexere Szenarios für kombinierte Attribute

  • Wenn eine Kombination der Attribute [SupportedOSPlatform] und [UnsupportedOSPlatform] vorliegt, werden alle Attribute nach dem Bezeichner der Betriebssystemplattform gruppiert:

    • Liste mit ausschließlich unterstützten Plattformen: Wenn die niedrigste Version jeder Betriebssystemplattform das Attribut [SupportedOSPlatform] aufweist, gilt die API als nur von den aufgelisteten Plattformen unterstützt und für alle anderen Plattformen als nicht unterstützt. Die optionalen [UnsupportedOSPlatform]-Attribute für die einzelnen Plattform können nur eine höhere Version der unterstützten Mindestversion aufweisen, was bedeutet, dass die API-Unterstützung ab der angegebenen Version entfernt wird.

      // API is only supported on Windows from version 6.2 to 10.0.19041.0 and all versions of Linux
      // The API is considered not supported for all other platforms.
      [SupportedOSPlatform("windows6.2")]
      [UnsupportedOSPlatform("windows10.0.19041.0")]
      [SupportedOSPlatform("linux")]
      public void ApiSupportedFromWindows80SupportFromCertainVersion();
      
    • Liste mit ausschließlich nicht unterstützten Plattformen: Wenn die niedrigste Version jeder Betriebssystemplattform das Attribut [UnsupportedOSPlatform] aufweist, gilt die API als nur von den aufgelisteten Plattformen nicht unterstützt und für alle anderen Plattformen als unterstützt. Die Liste kann das Attribut [SupportedOSPlatform] für die gleiche Plattform, aber eine höhere Version aufweisen, was bedeutet, dass die API ab dieser Version unterstützt wird.

      // The API is unsupported on all Linux versions was unsupported on Windows until version 10.0.19041.0.
      // The API is considered supported everywhere else without constraints.
      [UnsupportedOSPlatform("windows")]
      [SupportedOSPlatform("windows10.0.19041.0")]
      [UnsupportedOSPlatform("linux")]
      public void ApiSupportedFromWindows8UnsupportedFromWindows10();
      
    • Inkonsistente Liste: Wenn die niedrigste Version für einige Plattformen [SupportedOSPlatform], für andere Plattformen jedoch [UnsupportedOSPlatform] ist, wird die Liste als inkonsistent eingestuft. Dies wird vom Analysetool nicht unterstützt. Stellt das Analysetool eine Inkonsistenz fest, werden die [UnsupportedOSPlatform]-Plattformen ignoriert.

      • Wenn die niedrigsten Versionen der Attribute [SupportedOSPlatform] und [UnsupportedOSPlatform] gleich sind, gilt die Plattform für das Analysetool als Teil der Liste mit ausschließlich unterstützten Plattformen.
  • Plattformattribute können auf Typen, Member (Methoden, Felder, Eigenschaften und Ereignisse) und Assemblys mit einem anderen Plattformnamen oder einer anderen Version angewendet werden.

    • Attribute, die auf das oberste Ziel (target) angewendet werden, gelten infolge auch für alle Member und Typen.
    • Untergeordnete Attribute gelten nur, wenn sie die Regel „Untergeordnete Attribute können die Plattformunterstützung nur einschränken, nicht erweitern“ befolgen.
      • Wenn das übergeordnete Element eine Liste mit ausschließlich unterstützten Plattformen aufweist, können die untergeordneten Memberattribute keine neue Plattformunterstützung hinzufügen, da dies die Unterstützung für das übergeordnete Element erweitern würde. Neue Plattformunterstützung kann nur dem übergeordneten Element selbst hinzugefügt werden. Das untergeordnete Element kann jedoch das Attribut Supported für dieselbe Plattform mit höheren Versionen aufweisen, da dies die Unterstützung einschränkt. Ebenso kann das untergeordnete Element das Unsupported-Attribut mit derselben Plattform aufweisen, da dies die übergeordnete Unterstützung ebenfalls einschränkt.
      • Wenn das übergeordnete Element eine Liste mit ausschließlich nicht unterstützten Plattformen aufweist, können die untergeordneten Memberattribute Unterstützung für eine neue Plattform hinzufügen, da dies die übergeordnete Unterstützung einschränkt. Es kann jedoch nicht über das Supported-Attribut für dieselbe Plattform wie das übergeordnete Element verfügen, da dies die übergeordnete Unterstützung erweitert. Unterstützung für dieselbe Plattform kann dem übergeordneten Element nur in den Fällen hinzugefügt werden, in denen das ursprüngliche Unsupported-Attribut angewendet wurde.
    • Wenn [SupportedOSPlatform("platformVersion")] mehr als einmal auf eine API mit demselben platform-Namen angewendet wird, wird nur diejenige mit der Mindestversion vom Analysetool berücksichtigt.
    • Wenn [UnsupportedOSPlatform("platformVersion")] mehr als zweimal auf eine API mit demselben platform-Namen angewendet wird, werden nur die beiden mit den frühesten Versionen vom Analysetool berücksichtigt.

    Hinweis

    Wenn die Unterstützung für eine API zunächst vorhanden war und in einer höheren Version entfernt wurde, wird sie erwartungsgemäß in einer noch höheren Version nicht wieder eingeführt.

Beispiele für die Funktionsweise von Attributen und ihre Diagnosen

// An API supported only on Windows all versions.
[SupportedOSPlatform("Windows")]
public void WindowsOnlyApi() { }

// an API supported on Windows and Linux.
[SupportedOSPlatform("Windows")]
[SupportedOSPlatform("Linux")]
public void SupportedOnWindowsAndLinuxOnly() { }

// an API only supported on Windows 6.2 and later, not supported for all other.
// an API is removed/unsupported from version 10.0.19041.0.
[SupportedOSPlatform("windows6.2")]
[UnsupportedOSPlatform("windows10.0.19041.0")]
public void ApiSupportedFromWindows8UnsupportedFromWindows10() { }

// an Assembly supported on Windows, the API added from version 10.0.19041.0.
[assembly: SupportedOSPlatform("Windows")]
[SupportedOSPlatform("windows10.0.19041.0")]
public void AssemblySupportedOnWindowsApiSupportedFromWindows10() { }

public void Caller()
{
    WindowsOnlyApi(); // warns: This call site is reachable on all platforms. 'WindowsOnlyApi()' is only supported on: 'windows'

    // This call site is reachable on all platforms. 'SupportedOnWindowsAndLinuxOnly()' is only supported on: 'Windows', 'Linux'
    SupportedOnWindowsAndLinuxOnly();

    // This call site is reachable on all platforms. 'ApiSupportedFromWindows8UnsupportedFromWindows10()' is only supported on: 'windows' from version 6.2 to 10.0.19041.0
    ApiSupportedFromWindows8UnsupportedFromWindows10();

    // for same platform analyzer only warn for the latest version.
    // This call site is reachable on all platforms. 'AssemblySupportedOnWindowsApiSupportedFromWindows10()' is only supported on: 'windows' 10.0.19041.0 and later
    AssemblySupportedOnWindowsApiSupportedFromWindows10();
}

// an API not supported on android but supported on all other.
[UnsupportedOSPlatform("android")]
public void DoesNotWorkOnAndroid() { }

// an API was unsupported on Windows until version 6.2.
// The API is considered supported everywhere else without constraints.
[UnsupportedOSPlatform("windows")]
[SupportedOSPlatform("windows6.2")]
public void StartedWindowsSupportFromVersion8() { }

// an API was unsupported on Windows until version 6.2.
// Then the API is removed (unsupported) from version 10.0.19041.0.
// The API is considered supported everywhere else without constraints.
[UnsupportedOSPlatform("windows")]
[SupportedOSPlatform("windows6.2")]
[UnsupportedOSPlatform("windows10.0.19041.0")]
public void StartedWindowsSupportFrom8UnsupportedFrom10() { }

public void Caller2()
{
    DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'

    // This call site is reachable on all platforms. 'StartedWindowsSupportFromVersion8()' is unsupported on: 'windows' 6.2 and before.
    StartedWindowsSupportFromVersion8();

    // This call site is reachable on all platforms. 'StartedWindowsSupportFrom8UnsupportedFrom10()' is supported on: 'windows' from version 6.2 to 10.0.19041.0
    StartedWindowsSupportFrom8UnsupportedFrom10();
}

Verarbeiten gemeldeter Warnungen

Die empfohlene Umgangsweise mit diesen Diagnosen besteht darin, sicherzustellen, dass plattformspezifische APIs nur auf der passenden Plattform aufgerufen werden. Nachstehend sind Ihre Optionen für die Behebung dieser Warnungen aufgeführt. Wählen Sie die Vorgehensweise aus, die sich am besten für Ihre Situation eignet:

  • Schützen des Aufrufs: Zu diesem Zweck können Sie den Code bedingt zur Laufzeit aufrufen. Überprüfen Sie, ob die Ausführung auf einer gewünschten Platform erfolgt, indem Sie eine der Plattformprüfmethoden verwenden, z. B. OperatingSystem.Is<Platform>() oder OperatingSystem.Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0). Beispiel.

  • Markieren des Aufruforts als plattformspezifisch: Sie können auch Ihre eigenen APIs als plattformspezifisch kennzeichnen und die Anforderungen so effektiv an Ihre Aufrufer weiterleiten. Kennzeichnen Sie die enthaltende Methode, den enthaltenden Typ oder die gesamte Assembly mit denselben Attributen wie den referenzierten plattformabhängigen Aufruf. Beispiele.

  • Bestätigen des Aufruforts mit einer Plattformüberprüfung: Wenn Sie den Mehraufwand einer zusätzlichen if-Anweisung zur Laufzeit vermeiden möchten, verwenden Sie Debug.Assert(Boolean). Beispiel.

  • Löschen des Codes: Dies ist üblicherweise nicht die gewünschte Vorgehensweise, da Sie möglicherweise Kunden verlieren, wenn Ihr Code von Windows-Benutzern verwendet wird. Falls eine plattformübergreifende Alternative vorhanden ist, sollten Sie lieber diese anstelle der plattformspezifischen APIs wählen.

  • Unterdrücken der Warnung: Sie können die Warnung auch einfach unterdrücken, entweder über einen EditorConfig-Eintrag oder über #pragma warning disable CA1416. Diese Option sollte jedoch bei der Verwendung von plattformspezifischen APIs als allerletzte Maßnahme eingesetzt werden.

    Tipp

    Wenn Sie Warnungen mithilfe der vorkompilierten #pragma-Anweisungen deaktivieren, beachten Sie bei den Bezeichnern, die Sie als Ziel verwenden, die Groß-/Kleinschreibung. So wird z. B. mit ca1416 die Warnung CA1416 nicht deaktiviert.

Schützen von plattformspezifischen APIs mit Schutzmethoden

Der Plattformname der Schutzmethode muss mit dem aufrufenden plattformabhängigen API-Plattformnamen übereinstimmen. Wenn die Plattformzeichenfolge der aufrufenden API die Version enthält:

  • muss die Plattformversion (version) der Schutzmethode für das Attribut [SupportedOSPlatform("platformVersion")] größer oder gleich der Version der aufrufenden Plattform sein

  • muss die Plattformversion (version) der Schutzmethode für das Attribut [UnsupportedOSPlatform("platformVersion")] kleiner oder gleich der Version der aufrufenden Plattform sein

    public void CallingSupportedOnlyApis() // Allow list calls
    {
        if (OperatingSystem.IsWindows())
        {
            WindowsOnlyApi(); // will not warn
        }
    
        if (OperatingSystem.IsLinux())
        {
            SupportedOnWindowsAndLinuxOnly(); // will not warn, within one of the supported context
        }
    
        // Can use &&, || logical operators to guard combined attributes
        if (OperatingSystem.IsWindowsVersionAtLeast(6, 2) && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041)))
        {
            ApiSupportedFromWindows8UnsupportedFromWindows10();
        }
    
        if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041, 0))
        {
            AssemblySupportedOnWindowsApiSupportedFromWindows10(); // Only need to check latest supported version
        }
    }
    
    public void CallingUnsupportedApis()
    {
        if (!OperatingSystem.IsAndroid())
        {
            DoesNotWorkOnAndroid(); // will not warn
        }
    
        if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(6, 2))
        {
            StartedWindowsSupportFromVersion8(); // will not warn
        }
    
        if (!OperatingSystem.IsWindows() || // supported all other platforms
           (OperatingSystem.IsWindowsVersionAtLeast(6, 2) && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041)))
        {
            StartedWindowsSupportFrom8UnsupportedFrom10(); // will not warn
        }
    }
    
  • Wenn Code geschützt werden muss, der auf netstandard oder netcoreapp ausgerichtet ist, wo keine neuen OperatingSystem-APIs verfügbar sind, kann die API RuntimeInformation.IsOSPlatform verwendet werden. Diese wird auch vom Analysetool berücksichtigt. Sie ist jedoch nicht so optimiert, wie die neuen APIs, die über OperatingSystem hinzugefügt werden. Wenn die Plattform in der OSPlatform-Struktur nicht unterstützt wird, können Sie OSPlatform.Create(String) aufrufen und den Plattformnamen übergeben, der vom Analysetool ebenfalls berücksichtigt wird.

    public void CallingSupportedOnlyApis()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            SupportedOnWindowsAndLinuxOnly(); // will not warn
        }
    
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("browser")))
        {
            ApiOnlySupportedOnBrowser(); // call of browser specific API
        }
    }
    

Hinzufügen von Plattformwächterattributen zu APIs und Verwenden als benutzerdefinierten Wächter

Wie bereits beschrieben, erkennt das Analysetool die statischen Methoden zum Schutz von Plattformen im Typ OperatingSystem, z. B. OperatingSystem.IsWindows und RuntimeInformation.IsOSPlatform. Möglicherweise möchten Sie jedoch das Wächterergebnis in einem Feld für die Weiterverwendung zwischenspeichern oder benutzerdefinierte Wächtermethoden zur Überprüfung einer Plattform verwenden. Das Analysetool muss solche APIs als benutzerdefinierten Wächter erkennen und sollte nicht vor auf diese Weise geschützten APIs warnen. Die folgenden Wächterattribute wurden in .NET 6 für dieses Szenario eingeführt:

  • SupportedOSPlatformGuardAttribute für APIs, die als Wächter für APIs mit dem Attribut SupportedOSPlatformAttribute verwendet werden können.
  • UnsupportedOSPlatformGuardAttribute für APIs, die als Wächter für APIs mit dem Attribut UnsupportedOSPlatformAttribute verwendet werden können.

Diese Attribute können optional eine Versionsnummer enthalten. Sie können mehrmals angewendet werden, um weitere Plattformen zu schützen sowie als Attribut für ein Feld, eine Eigenschaft oder eine Methode verwendet werden.

class Test
{
    [UnsupportedOSPlatformGuard("browser")] // The platform guard attribute
#if TARGET_BROWSER
    internal bool IsSupported => false;
#else
    internal bool IsSupported => true;
#endif

    [UnsupportedOSPlatform("browser")]
    void ApiNotSupportedOnBrowser() { }

    void M1()
    {
        ApiNotSupportedOnBrowser();  // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'

        if (IsSupported)
        {
            ApiNotSupportedOnBrowser();  // Not warn
        }
    }

    [SupportedOSPlatform("Windows")]
    [SupportedOSPlatform("Linux")]
    void ApiOnlyWorkOnWindowsLinux() { }

    [SupportedOSPlatformGuard("Linux")]
    [SupportedOSPlatformGuard("Windows")]
    private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();

    void M2()
    {
        ApiOnlyWorkOnWindowsLinux();  // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.

        if (_isWindowOrLinux)
        {
            ApiOnlyWorkOnWindowsLinux();  // Not warn
        }
    }
}

Markieren des Aufruforts als plattformspezifisch

Plattformnamen sollten mit der aufrufenden plattformabhängigen API übereinstimmen. Wenn die Plattformzeichenfolge eine Version enthält:

  • muss die Plattformversion (version) des Aufruforts für das Attribut [SupportedOSPlatform("platformVersion")] größer oder gleich der Version der aufrufenden Plattform sein

  • muss die Plattformversion (version) des Aufruforts für das Attribut [UnsupportedOSPlatform("platformVersion")] kleiner oder gleich der Version der aufrufenden Plattform sein

    // an API supported only on Windows.
    [SupportedOSPlatform("windows")]
    public void WindowsOnlyApi() { }
    
    // an API supported on Windows and Linux.
    [SupportedOSPlatform("Windows")]
    [SupportedOSPlatform("Linux")]
    public void SupportedOnWindowsAndLinuxOnly() { }
    
    // an API only supported on Windows 6.2 and later, not supported for all other.
    // an API is removed/unsupported from version 10.0.19041.0.
    [SupportedOSPlatform("windows6.2")]
    [UnsupportedOSPlatform("windows10.0.19041.0")]
    public void ApiSupportedFromWindows8UnsupportedFromWindows10() { }
    
    // an Assembly supported on Windows, the API added from version 10.0.19041.0.
    [assembly: SupportedOSPlatform("Windows")]
    [SupportedOSPlatform("windows10.0.19041.0")]
    public void AssemblySupportedOnWindowsApiSupportedFromWindows10() { }
    
    [SupportedOSPlatform("windows6.2")] // call site attributed Windows 6.2 or above.
    public void Caller()
    {
        WindowsOnlyApi(); // will not warn as call site is for Windows.
    
        // will not warn as call site is for Windows all versions.
        SupportedOnWindowsAndLinuxOnly();
    
        // will not warn for the [SupportedOSPlatform("windows6.2")] attribute, but warns for [UnsupportedOSPlatform("windows10.0.19041.0")]
        // This call site is reachable on: 'windows' 6.2 and later. 'ApiSupportedFromWindows8UnsupportedFromWindows10()' is unsupported on: 'windows' 10.0.19041.0 and later.
        ApiSupportedFromWindows8UnsupportedFromWindows10();
    
        // The call site version is lower than the calling version, so warns:
        // This call site is reachable on: 'windows' 6.2 and later. 'AssemblySupportedOnWindowsApiSupportedFromWindows10()' is only supported on: 'windows' 10.0.19041.0 and later
        AssemblySupportedOnWindowsApiSupportedFromWindows10();
    }
    
    [SupportedOSPlatform("windows10.0.22000")] // call site attributed with windows 10.0.22000 or above.
    public void Caller2()
    {
        // This call site is reachable on: 'windows' 10.0.22000 and later. 'ApiSupportedFromWindows8UnsupportedFromWindows10()' is unsupported on: 'windows' 10.0.19041.0 and later.
        ApiSupportedFromWindows8UnsupportedFromWindows10();
    
        // will not warn as call site version higher than calling API.
        AssemblySupportedOnWindowsApiSupportedFromWindows10();
    }
    
    [SupportedOSPlatform("windows6.2")]
    [UnsupportedOSPlatform("windows10.0.19041.0")] // call site supports Windows from version 6.2 to 10.0.19041.0.
    public void Caller3()
    {
        // will not warn as caller has exact same attributes.
        ApiSupportedFromWindows8UnsupportedFromWindows10();
    
        // The call site reachable for the version not supported in the calling API, therefore warns:
        // This call site is reachable on: 'windows' from version 6.2 to 10.0.19041.0. 'AssemblySupportedOnWindowsApiSupportedFromWindows10()' is only supported on: 'windows' 10.0.19041.0 and later.
        AssemblySupportedOnWindowsApiSupportedFromWindows10();
    }
    
    // an API not supported on Android but supported on all other.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // an API was unsupported on Windows until version 6.2.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows6.2")]
    public void StartedWindowsSupportFromVersion8() { }
    
    // an API was unsupported on Windows until version 6.2.
    // Then the API is removed (unsupported) from version 10.0.19041.0.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows6.2")]
    [UnsupportedOSPlatform("windows10.0.19041.0")]
    public void StartedWindowsSupportFrom8UnsupportedFrom10() { }
    
    [UnsupportedOSPlatform("windows")] // Caller no support Windows for any version.
    public void Caller4()
    {
        // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
        DoesNotWorkOnAndroid();
    
        // will not warns as the call site not support Windows at all, but supports all other.
        StartedWindowsSupportFromVersion8();
    
        // same, will not warns as the call site not support Windows at all, but supports all other.
        StartedWindowsSupportFrom8UnsupportedFrom10();
    }
    
    [UnsupportedOSPlatform("windows")]
    [UnsupportedOSPlatform("android")] // Caller not support Windows and Android for any version.
    public void Caller4()
    {
        DoesNotWorkOnAndroid(); // will not warn as call site not supports Android.
    
        // will not warns as the call site not support Windows at all, but supports all other.
        StartedWindowsSupportFromVersion8();
    
        // same, will not warns as the call site not support Windows at all, but supports all other.
        StartedWindowsSupportFrom8UnsupportedFrom10();
    }
    

Bestätigen des Aufruforts mit einer Plattformüberprüfung

Alle in den Beispielen für den Plattformschutz verwendeten bedingten Überprüfungen können auch als Bedingung in Debug.Assert(Boolean) eingefügt werden.

// An API supported only on Linux.
[SupportedOSPlatform("linux")]
public void LinuxOnlyApi() { }

public void Caller()
{
    Debug.Assert(OperatingSystem.IsLinux());

    LinuxOnlyApi(); // will not warn
}

Weitere Informationen