CA1416: Plattformkompatibilität überprüfen

Eigenschaft Wert
Regel-ID CA1416
Titel Plattformkompatibilität überprüfen
Kategorie Interoperabilität
Fix führt oder führt nicht zur Unterbrechung Nicht unterbrechend
Standardmäßig in .NET 8 aktiviert Als Warnung

Ursache

Verstöße werden gemeldet, wenn eine plattformspezifische API im Kontext einer anderen Plattform verwendet wird oder wenn die Plattform nicht verifiziert ist (plattformneutral). Verstöße werden auch gemeldet, wenn eine API verwendet wird, die für die Zielplattform des Projekts nicht unterstützt wird.

Diese Regel ist standardmäßig nur für Projekte aktiviert, die auf .NET 5 oder höher ausgerichtet sind. Sie können es jedoch auch für Projekte aktivieren, die auf andere Frameworks abzielen.

Regelbeschreibung

Mit .NET 5 wurden neue Attribute hinzugefügt – SupportedOSPlatformAttribute und UnsupportedOSPlatformAttribute –, um plattformspezifische APIs mit Anmerkungen zu versehen. Beide Attribute können mit oder ohne Versionsnummern als Teil des Plattformnamens instanziiert werden. Sie können auch mehrere Male auf verschiedenen Plattformen angewendet werden.

  • Es wird davon ausgegangen, dass eine API ohne Anmerkungen auf allen Betriebssystemplattformen funktioniert.
  • Eine API mit der Anmerkung [SupportedOSPlatform("platformName")] wird nur als portierbar für die angegebenen Betriebssystemplattformen angesehen. Wenn die Plattform eine Teilmenge einer anderen Plattform ist, impliziert das Attribut, dass die Plattform ebenfalls unterstützt wird.
  • Eine API mit der Anmerkung [UnsupportedOSPlatform("platformName")] wird auf den angegebenen Betriebssystemplattformen als nicht unterstützt angesehen. Wenn die Plattform eine Teilmenge einer anderen Plattform ist, impliziert das Attribut, dass die Plattform ebenfalls nicht unterstützt wird.

Sie können die Attribute [SupportedOSPlatform] und [UnsupportedOSPlatform] für eine einzelne API kombinieren. In diesem Fall gelten die folgenden Regeln:

  • Positivliste. 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 Liste kann ein [UnsupportedOSPlatform]-Attribut mit der gleichen Plattform, aber nur mit einer höheren Version aufweisen, was bedeutet, dass die API aus dieser Version entfernt wurde.
  • Verweigerungsliste. 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 ein [SupportedOSPlatform]-Attribut mit der gleichen Plattform, aber nur mit einer höheren Version aufweisen, was bedeutet, dass die API seit dieser Version unterstützt wird.
  • Inkonsistente Liste: Wenn die niedrigste Version für einige Plattformen [SupportedOSPlatform] und für andere [UnsupportedOSPlatform] lautet, wird diese Kombination als uneinheitlich angesehen. Einige Anmerkungen in der API werden ignoriert. In Zukunft wird möglicherweise ein Analysetool eingeführt, das bei Inkonsistenzen eine Warnung ausgibt.

Wenn Sie im Kontext einer anderen Plattform auf eine API zugreifen, die mit diesen Attributen versehen ist, werden CA1416-Verstöße angezeigt.

TFM-Zielplattformen

Das Analyseprogramm überprüft keine Zielplattformen des Zielframeworkmonikers (TFM) aus MSBuild-Eigenschaften, z. B. <TargetFramework> oder <TargetFrameworks>. Wenn der TFM über eine Zielplattform verfügt, fügt das .NET SDK 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 lautet, fügt das SDK das [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")]-Attribut in die Datei AssemblyInfo.cs ein, und die gesamte Assembly wird als reine Windows-Assembly betrachtet. Daher würde der Aufruf von reinen Windows-APIs mit Version 7.0 oder niedriger keine Warnungen im Projekt verursachen.

Hinweis

Wenn die Generierung der AssemblyInfo.cs-Datei für das Projekt deaktiviert ist (d. h., die Eigenschaft <GenerateAssemblyInfo> ist auf false festgelegt), kann das erforderliche SupportedOSPlatform-Attribut auf Assemblyebene vom SDK nicht 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.

Verletzungen

  • Wenn Sie über Code, der auf anderen Plattformen erreichbar ist, auf eine API zugreifen, die nur auf einer angegebenen Plattform ([SupportedOSPlatform("platformName")]) unterstützt wird, wird die folgende Verletzung angezeigt: 'API' is supported on 'platformName' ('API' wird auf 'platformName' unterstützt) .

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    

    Hinweis

    Ein Verstoß tritt nur auf, wenn das Projekt nicht auf die unterstützte Plattform (net5.0-differentPlatform) ausgerichtet ist. Dies gilt auch für Projekte mit mehreren Zielen. Es tritt kein Verstoß auf, wenn das Projekt die angegebene Plattform (net5.0-platformName) als Ziel verwendet und die Generierung der Datei AssemblyInfo.cs für das Projekt aktiviert ist.

  • Der Zugriff auf eine API, die mit [UnsupportedOSPlatform("platformName")] attributiert ist, aus einem Kontext, der auf die nicht unterstützte Plattform abzielt, kann zu einem Verstoß führen: 'API' is unsupported on 'platformName' ('API' wird auf 'platformName' nicht unterstützt) .

    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion() { }
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before
    }
    

Hinweis

Wenn Sie eine App entwickeln, die nicht auf die nicht unterstützte Plattform ausgerichtet ist, erhalten Sie keine Verstöße. Ein Verstoß tritt nur in den folgenden Fällen auf:

  • Das Projekt zielt auf die Plattform ab, die als nicht unterstützt eingestuft wird.

  • platformName ist in der Standardelementgruppe MSBUILD<SupportedPlatform> enthalten.

  • platformName wurde der MSBuild-Elementgruppe <SupportedPlatform> manuell hinzugefügt.

    <ItemGroup>
        <SupportedPlatform Include="platformName" />
    </ItemGroup>
    

Behandeln von Verstößen

Die empfohlene Umgangsweise mit diesen Verstößen besteht im Sicherstellen, dass plattformspezifische APIs nur auf der geeigneten Plattform aufgerufen werden. Sie können dies erreichen, indem Sie den Code zur Buildzeit mit #if ausschließen und mehrere Ziele angeben, oder indem Sie den Code zur Laufzeit bedingt aufrufen. Das Analysetool erkennt die Plattformwächter in der OperatingSystem-Klasse und in System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Unterdrücken Sie Verstöße, indem Sie die Aufrufwebsite mit den standardmäßigen Plattformschutzmethoden oder benutzerdefinierten Schutz-APIs umgeben, die mit SupportedOSPlatformGuardAttribute oder UnsupportedOSPlatformGuardAttribute versehen sind.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'.
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    
    [SupportedOSPlatformGuard("windows")]  // The platform guard attributes used
    [SupportedOSPlatformGuard("ios14.0")]
    private readonly bool _isWindowOrIOS14 = OperatingSystem.IsWindows() || OperatingSystem.IsIOSVersionAtLeast(14);
    
    // The warnings are avoided using platform guard methods.
    public void Caller()
    {
        if (OperatingSystem.IsLinux()) // standard guard examples
        {
            LinuxOnlyApi(); // no diagnostic
        }
    
        if (OperatingSystem.IsIOSVersionAtLeast(14))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (_isWindowOrMacOS14) // custom guard example
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    }
    
    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before.
    }
    
    [UnsupportedOSPlatformGuard("android")] // The platform guard attribute
    bool IsNotAndroid => !OperatingSystem.IsAndroid();
    
    public void Caller()
    {
        if (!OperatingSystem.IsAndroid()) // using standard guard methods
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    
        // Use the && and || logical operators to guard combined attributes.
        if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18362))
        {
            StartedWindowsSupportFromCertainVersion(); // no diagnostic
        }
    
        if (IsNotAndroid) // custom guard example
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    }
    
  • Das Analysetool respektiert auch System.Diagnostics.Debug.Assert als Mittel zum Verhindern, dass der Code auf nicht unterstützten Plattformen erreicht wird. Mithilfe von Debug.Assert kann die Überprüfung bei Bedarf aus Releasebuilds entfernt werden.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • Sie können Ihre eigenen APIs als plattformspezifisch kennzeichnen und die Anforderungen effektiv an Ihre Aufrufer weiterleiten. Sie können Plattformattribute auf die folgenden APIs anwenden:

    • Typen
    • Member (Methoden, Felder, Eigenschaften und Ereignisse)
    • Assemblys
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")]
    public void SupportedOnWindowsAndIos14() { }
    
    [SupportedOSPlatform("ios15.0")] // call site version should be equal to or higher than the API version
    public void Caller()
    {
        SupportedOnWindowsAndIos14(); // No diagnostics
    }
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void Caller()
    {
        StartedWindowsSupportFromCertainVersion(); // No diagnostics
    }
    
  • Wenn ein Attribut auf Assembly- oder Typebene angewendet wird, werden alle Member innerhalb der Assembly oder des Typs als plattformspezifisch betrachtet.

    [assembly:SupportedOSPlatform("windows")]
    public namespace ns
    {
        public class Sample
        {
            public void SupportedOnWindows() { }
    
            public void Caller()
            {
                SupportedOnWindows(); // No diagnostic as call site and calling method both windows only
            }
        }
    }
    

Wann sollten Warnungen unterdrückt werden?

Das Verweisen auf plattformspezifische APIs ohne einen entsprechenden Plattformkontext oder Wächter ist nicht empfehlenswert. Sie können diese Diagnose jedoch unterdrücken, indem Sie #pragma oder das NoWarn-Compilerflag verwenden oder indem Sie den Schweregrad der Regel in einer .editorconfig-Datei auf none festlegen.

[SupportedOSPlatform("linux")]
public void LinuxOnlyApi() { }

public void Caller()
{
#pragma warning disable CA1416
    LinuxOnlyApi();
#pragma warning restore CA1416
}

Konfigurieren des zu analysierenden Codes

Das Analysetool ist standardmäßig nur für Projekte aktiviert, die auf .NET 5 oder höher abzielen und ein AnalysisLevel von 5 oder höher aufweisen. Sie können es für Zielframeworks kleiner als net5.0 aktivieren, indem Sie das folgende Schlüssel-Wert-Paar einer EDITORCONFIG-Datei in Ihrem Projekt hinzufügen:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Siehe auch