CA1416: Weryfikowanie zgodności platformy

Właściwości Wartość
Identyfikator reguły CA1416
Tytuł Weryfikowanie zgodności platformy
Kategoria Współdziałanie
Poprawka powodująca niezgodność lub niezgodność Niezgodność
Domyślnie włączone na platformie .NET 8 Jako ostrzeżenie

Przyczyna

Naruszenia są zgłaszane, jeśli interfejs API specyficzny dla platformy jest używany w kontekście innej platformy lub jeśli platforma nie jest weryfikowana (neutralna dla platformy). Naruszenia są również zgłaszane, jeśli jest używany interfejs API, który nie jest obsługiwany dla platformy docelowej projektu.

Ta reguła jest domyślnie włączona tylko dla projektów przeznaczonych dla platformy .NET 5 lub nowszej. Można go jednak włączyć dla projektów przeznaczonych dla innych platform.

Opis reguły

Platforma .NET 5 dodała nowe atrybuty SupportedOSPlatformAttribute i UnsupportedOSPlatformAttribute, aby dodawać adnotacje do interfejsów API specyficznych dla platformy. Oba atrybuty mogą być tworzone przy użyciu numerów wersji lub bez ich w ramach nazwy platformy. Można je również wielokrotnie stosować na różnych platformach.

  • Interfejs API bez adnotacji jest uznawany za działający na wszystkich platformach systemu operacyjnego.
  • Interfejs API oznaczony za pomocą [SupportedOSPlatform("platformName")] jest uważany za przenośny tylko do określonych platform systemu operacyjnego. Jeśli platforma jest podzbiorem innej platformy, atrybut oznacza, że ta platforma jest również obsługiwana.
  • Interfejs API oznaczony za pomocą [UnsupportedOSPlatform("platformName")] jest uznawany za nieobsługiwany na określonych platformach systemu operacyjnego. Jeśli platforma jest podzbiorem innej platformy, atrybut oznacza, że ta platforma również nie jest obsługiwana.

Można łączyć [SupportedOSPlatform] atrybuty i [UnsupportedOSPlatform] w jednym interfejsie API. W takim przypadku obowiązują następujące reguły:

  • Lista dozwolonych. Jeśli najniższa wersja dla każdej platformy systemu operacyjnego jest atrybutem [SupportedOSPlatform] , interfejs API jest uznawany za obsługiwany tylko przez wymienione platformy i nieobsługiwane przez wszystkie inne platformy. Lista może mieć [UnsupportedOSPlatform] atrybut z tą samą platformą, ale tylko z wyższą wersją, co oznacza, że interfejs API został usunięty z tej wersji.
  • Lista odmowy. Jeśli najniższa wersja dla każdej platformy systemu operacyjnego jest atrybutem [UnsupportedOSPlatform] , interfejs API jest uznawany za nieobsługiwany tylko przez wymienione platformy i obsługiwany przez wszystkie inne platformy. Lista może mieć atrybut z tą samą platformą [SupportedOSPlatform] , ale tylko z wyższą wersją, co oznacza, że interfejs API jest obsługiwany od tej wersji.
  • Niespójna lista. Jeśli najniższa wersja dla niektórych platform jest [SupportedOSPlatform] ale [UnsupportedOSPlatform] dla innych platform, ta kombinacja jest uważana za niespójną. Niektóre adnotacje interfejsu API są ignorowane. W przyszłości możemy wprowadzić analizator, który generuje ostrzeżenie w przypadku niespójności.

Jeśli uzyskujesz dostęp do interfejsu API oznaczonego za pomocą tych atrybutów z kontekstu innej platformy, możesz zobaczyć naruszenia ca1416.

Platformy docelowe TFM

Analizator nie sprawdza platform docelowych moniker (TFM) platform docelowych z właściwości programu MSBuild, takich jak <TargetFramework> lub <TargetFrameworks>. Jeśli serwer TFM ma platformę docelową, zestaw .NET SDK wprowadza SupportedOSPlatform atrybut z docelową nazwą platformy w pliku AssemblyInfo.cs , który jest używany przez analizator. Jeśli na przykład serwer TFM to net5.0-windows10.0.19041, zestaw SDK wprowadza [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] atrybut do pliku AssemblyInfo.cs , a cały zestaw jest uznawany tylko za system Windows. W związku z tym wywoływanie interfejsów API tylko dla systemu Windows w wersji 7.0 lub nowszej nie spowoduje żadnych ostrzeżeń w projekcie.

Uwaga

Jeśli generowanie plików AssemblyInfo.cs jest wyłączone dla projektu (czyli <GenerateAssemblyInfo> właściwość jest ustawiona na false), wymagany atrybut poziomu SupportedOSPlatform zestawu nie może zostać dodany przez zestaw SDK. W takim przypadku można zobaczyć ostrzeżenia dotyczące użycia interfejsów API specyficznych dla platformy, nawet jeśli jest przeznaczona dla tej platformy. Aby rozwiązać problemy z ostrzeżeniami, włącz generowanie plików AssemblyInfo.cs lub dodaj atrybut ręcznie w projekcie.

Naruszenia regulaminu

  • Jeśli uzyskujesz dostęp do interfejsu API obsługiwanego tylko na określonej platformie ([SupportedOSPlatform("platformName")]) z poziomu kodu dostępnego na innych platformach, zobaczysz następujące naruszenie: "INTERFEJS API" jest obsługiwany na platformie "platformName".

    // 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.
    }
    

    Uwaga

    Naruszenie występuje tylko wtedy, gdy projekt nie jest przeznaczony dla obsługiwanej platformy (net5.0-differentPlatform). Dotyczy to również projektów wielokierunkowych. Brak naruszenia, jeśli projekt jest przeznaczony dla określonej platformy (net5.0-platformName), a generowanie plików AssemblyInfo.cs jest włączone dla projektu.

  • Uzyskiwanie dostępu do interfejsu API przypisanego z [UnsupportedOSPlatform("platformName")] kontekstu przeznaczonego dla nieobsługiwanej platformy może spowodować naruszenie: "INTERFEJS API" jest nieobsługiwany na platformie "platformName".

    // 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
    }
    

Uwaga

Jeśli tworzysz aplikację, która nie jest ukierunkowana na nieobsługiwaną platformę, nie otrzymasz żadnych naruszeń. Naruszenie występuje tylko w następujących przypadkach:

  • Projekt jest przeznaczony dla platformy, która jest przypisywana jako nieobsługiwana.

  • Element platformName jest uwzględniony w domyślnej grupie elementów MSBuild <SupportedPlatform> .

  • platformName element jest dołączany ręcznie do grupy elementów programu MSBuild <SupportedPlatform> .

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

Jak naprawić naruszenia

Zalecanym sposobem radzenia sobie z naruszeniami jest upewnienie się, że podczas uruchamiania na odpowiedniej platformie są wywoływane tylko interfejsy API specyficzne dla platformy. Można to osiągnąć, wykluczając kod w czasie kompilacji przy użyciu funkcji #if i wielowersyjność lub warunkowo wywołując kod w czasie wykonywania. Analizator rozpoznaje strażniki platformy w OperatingSystem klasie i System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Pomijanie naruszeń przez otaczanie witryny wywołania za pomocą standardowych metod ochrony platformy lub niestandardowych interfejsów API ochrony z adnotacjami SupportedOSPlatformGuardAttribute lub UnsupportedOSPlatformGuardAttribute.

    // 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
        }
    }
    
  • Analizator uwzględnia również jako środek uniemożliwiający System.Diagnostics.Debug.Assert osiągnięcie kodu na nieobsługiwanych platformach. Użycie Debug.Assert umożliwia sprawdzanie przycinania kompilacji wydania, jeśli jest to konieczne.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • Możesz oznaczyć własne interfejsy API jako specyficzne dla platformy, efektywnie przekazując wymagania do osób wywołujących. Atrybuty platformy można zastosować do dowolnego z następujących interfejsów API:

    • Typy
    • Elementy członkowskie (metody, pola, właściwości i zdarzenia)
    • Zestawy
    [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
    }
    
  • Po zastosowaniu atrybutu na poziomie zestawu lub na poziomie typu wszystkie elementy członkowskie w zestawie lub typie są uważane za specyficzne dla platformy.

    [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
            }
        }
    }
    

Kiedy pomijać ostrzeżenia

Nie zaleca się odwoływania się do interfejsów API specyficznych dla platformy bez odpowiedniego kontekstu platformy lub ochrony. Można jednak pominąć tę diagnostykę przy użyciu #pragma flagi kompilatora NoWarn lub ustawiając ważność reguły na none w pliku editorconfig.

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

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

Konfigurowanie kodu do analizowania

Analizator jest domyślnie włączony tylko dla projektów przeznaczonych dla platformy .NET 5 lub nowszej i ma wartość AnalysisLevel o wartości 5 lub nowszej. Można ją włączyć dla platform docelowych niższych niż net5.0 przez dodanie następującej pary klucz-wartość do pliku .editorconfig w projekcie:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Zobacz też