Udostępnij przez


Analizator zgodności platformy

.NET to pojedyncza, ujednolicona platforma do tworzenia aplikacji dowolnego typu. Stara się zapewnić środowisko, w którym nie musisz rozumować o różnych odmianach platformy .NET, ale nie próbuje w pełni wyodrębnić bazowego systemu operacyjnego. Możesz nadal wywoływać interfejsy API specyficzne dla platformy, na przykład P/Invokes i WinRT.

Jednak użycie interfejsów API specyficznych dla platformy w składniku oznacza, że kod nie działa już na wszystkich platformach. Analizator zgodności platformy i uzupełniające interfejsy API zapewniają diagnostykę, aby ułatwić identyfikowanie i używanie interfejsów API specyficznych dla platformy, jeśli jest to konieczne.

Uzupełniające interfejsy API obejmują:

  • SupportedOSPlatformAttribute do dodawania adnotacji do interfejsów API jako specyficznych dla platformy i UnsupportedOSPlatformAttribute dodawania adnotacji do interfejsów API jako nieobsługiwanych w określonym systemie operacyjnym. Te atrybuty mogą opcjonalnie zawierać numer wersji i zostały już zastosowane do niektórych interfejsów API specyficznych dla platformy w podstawowych bibliotekach platformy .NET.
  • Is<Platform>() i Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) metody statyczne klasy System.OperatingSystem do bezpiecznego wywoływania platformowych interfejsów API. Na przykład, OperatingSystem.IsWindows() można użyć, aby chronić wywołanie interfejsu API specyficznego dla systemu Windows, a OperatingSystem.IsWindowsVersionAtLeast() można użyć, aby chronić wersjonowane wywołanie interfejsu API specyficznego dla systemu Windows. Zobacz te przykłady, jak te metody mogą być używane jako zabezpieczenia odniesień API specyficznych dla platformy.

Wymagania wstępne

Analizator zgodności platformy jest jednym z analizatorów jakości kodu Roslyn. Te analizatory są dołączone do zestawu .NET SDK. Analizator zgodności platformy jest domyślnie włączony tylko dla projektów ukierunkowanych na net5.0 lub nowsze wersje. Można go jednak włączyć dla projektów przeznaczonych dla innych platform.

Jak analizator określa zależność platformy

  • Nieprzypisany interfejs API uważa się za działający na wszystkich platformach OS.

  • Interfejs API oznaczony jako [SupportedOSPlatform("platform")] jest uznawany za przenośny tylko dla określonej platformy i wszystkich platform, których jest podzbiorem.

    • Atrybut można stosować wiele razy, aby wskazać obsługę wielu platform, na przykład [SupportedOSPlatform("windows"), SupportedOSPlatform("Android29.0")].
    • Jeśli platforma jest podzbiorem innej platformy, atrybut oznacza, iż nadzbiorowa platforma jest również obsługiwana. Na przykład [SupportedOSPlatform("iOS")] oznacza to, że interfejs API jest obsługiwany na platformie iOS oraz na platformie, która jest superzbiorem MacCatalyst.
    • Analizator wygeneruje ostrzeżenie , jeśli odwołania do interfejsów API specyficznych dla platformy są przywołyne bez odpowiedniego kontekstu platformy:
      • Ostrzega , jeśli projekt nie jest przeznaczony dla obsługiwanej platformy (na przykład interfejs API specyficzny dla systemu Windows wywoływany z projektu przeznaczonego dla systemu iOS <TargetFramework>net5.0-ios14.0</TargetFramework>).
      • Ostrzega , czy projekt jest międzyplatformowy i wywołuje interfejsy API specyficzne dla platformy (na przykład interfejs API specyficzny dla systemu Windows wywoływany z międzyplatformowego programu TFM <TargetFramework>net5.0</TargetFramework>).
      • Nie ostrzega , jeśli interfejs API specyficzny dla platformy jest przywołytyny w projekcie przeznaczonym dla dowolnej z określonych platform (na przykład w przypadku interfejsu API specyficznego dla systemu Windows wywoływanego z okien <TargetFramework>net5.0-windows</TargetFramework> docelowych projektu, a generowanie plików AssemblyInfo.cs jest włączone dla projektu).
      • Nie ostrzega, jeśli wywołanie interfejsu API specyficznego dla platformy jest zabezpieczone odpowiednimi metodami sprawdzania platformy (na przykład wywołanie interfejsu API specyficznego dla systemu Windows zabezpieczone przez OperatingSystem.IsWindows()).
      • Nie ostrzega, jeśli interfejs API specyficzny dla platformy jest wywoływany z tego samego kontekstu specyficznego dla platformy (lokacja wywołania również posiada atrybut[SupportedOSPlatform("platform")).
  • Interfejs API oznaczony za pomocą [UnsupportedOSPlatform("platform")] jest uważany za nieobsługiwany na określonej platformie i wszystkich platformach, których jest podzbiorem, ale obsługiwany dla wszystkich innych platform.

    • Atrybut można stosować wiele razy z różnymi platformami, na przykład [UnsupportedOSPlatform("iOS"), UnsupportedOSPlatform("Android29.0")].
    • Jeśli platforma jest podzbiorem innej platformy, atrybut oznacza, że większa platforma jest również nieobsługiwana. Na przykład [UnsupportedOSPlatform("iOS")] oznacza, że interfejs API jest nieobsługiwany na platformie iOS i na jej platformie nadzbiorczej MacCatalyst.
    • Analizator generuje ostrzeżenie tylko wtedy, gdy platform jest skuteczny dla miejsca wywołania.
      • Ostrzega jeśli projekt jest przeznaczony dla platformy, która jest oznaczona jako nieobsługiwana (na przykład, jeśli interfejs API jest oznaczony jako [UnsupportedOSPlatform("windows")], a miejsce wywołania jest przeznaczone dla <TargetFramework>net5.0-windows</TargetFramework>).

      • Ostrzega , czy projekt jest wielokierunkowy, a platform element jest uwzględniony w domyślnej grupie elementów programu MSBuild <SupportedPlatform> lub platform jest ręcznie uwzględniony w MSBuild<SupportedPlatform> grupie elementów:

        <ItemGroup>
            <SupportedPlatform Include="platform" />
        </ItemGroup>
        
      • Nie ostrzega , jeśli tworzysz aplikację, która nie jest przeznaczona dla nieobsługiwanej platformy lub jest wielokierunkowa, a platforma nie jest uwzględniona w domyślnej grupie elementów programu MSBuild <SupportedPlatform> .

  • Oba atrybuty mogą być tworzone przy użyciu numerów wersji lub bez ich w ramach nazwy platformy. Numery wersji są w formacie major.minor[.build[.revision]]; major.minor jest wymagane, a części build i revision są opcjonalne. Na przykład "Windows6.1" wskazuje system Windows w wersji 6.1, ale "Windows" jest interpretowany jako Windows 0.0.

Aby uzyskać więcej informacji, zobacz przykłady działania atrybutów i ich diagnostyki.

Jak analizator rozpoznaje 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 program TFM ma platformę docelową, program MSBuild wprowadza SupportedOSPlatform atrybut z docelową nazwą platformy w pliku AssemblyInfo.cs , który jest używany przez analizator. Na przykład, jeśli TFM to net5.0-windows10.0.19041, program MSBuild wprowadza atrybut [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] do pliku AssemblyInfo.cs, a cały zestaw jest uznawany za wyłącznie 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 / Notatka

Jeśli generowanie plików AssemblyInfo.cs jest wyłączone dla projektu (czyli <GenerateAssemblyInfo> właściwość jest ustawiona na false), nie można dodać wymaganego atrybutu poziomu SupportedOSPlatform zestawu przez program MSBuild. 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.

Włączenie platformy

Platforma .NET 6 wprowadziła koncepcję dołączania platformy, gdzie jedna platforma może być podzbiorem innej platformy. Adnotacja dla platformy podzbioru oznacza tę samą obsługę (lub jej brak) dla platformy superzbioru. Jeśli metoda sprawdzania platformy w typie OperatingSystem ma atrybut SupportedOSPlatformGuard("supersetPlatform")], to supersetPlatform uznaje się za nadzbiór platformy systemu operacyjnego, którą sprawdza ta metoda.

Na przykład metoda OperatingSystem.IsIOS() jest przypisywana[SupportedOSPlatformGuard("MacCatalyst")]. W związku z tym mają zastosowanie następujące stwierdzenia:

  • Metody OperatingSystem.IsIOS() i OperatingSystem.IsIOSVersionAtLeast sprawdzają nie tylko platformę iOS , ale także platformę MacCatalyst .
  • [SupportedOSPlatform("iOS")] oznacza, że interfejs API jest obsługiwany na platformie iOS , a także na jej superzestawowej MacCatalystplatformie . Możesz użyć atrybutu [UnsupportedOSPlatform("MacCatalyst")], aby wykluczyć tę dorozumianą obsługę.
  • [UnsupportedOSPlatform("iOS") oznacza, że interfejs API nie jest obsługiwany w systemach iOS i MacCatalyst. Możesz użyć atrybutu [SupportedOSPlatform("MacCatalyst")] , aby wykluczyć ten domniemany brak obsługi.

Rozważ poniższą macierz pokrycia, w której ✔️ wskazuje, że platforma jest obsługiwana i ❌ wskazuje, że platforma nie jest obsługiwana.

Platforma SupportedOSPlatform(subset) SupportedOSPlatform(superset) UnsupportedOSPlatform(subset) UnsupportedOSPlatform(superset)
Podzbiór ✔️ ✔️
Nadzbiór ✔️ ✔️ ✔️ ✔️

Wskazówka

Te same reguły dotyczą atrybutów SupportedOSPlatformGuard i UnsupportedOSPlatformGuard .

Poniższy fragment kodu pokazuje, jak połączyć atrybuty w celu ustawienia odpowiedniego poziomu obsługi.

  // 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() { }

Zaawansowane scenariusze kombinacji atrybutów

  • Jeśli istnieje kombinacja atrybutów [SupportedOSPlatform] i [UnsupportedOSPlatform] , wszystkie atrybuty są pogrupowane według identyfikatora platformy systemu operacyjnego:

    • Obsługiwana jest tylko lista. 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. Opcjonalne [UnsupportedOSPlatform] atrybuty dla każdej platformy mogą mieć tylko wyższą wersję niż minimalna obsługiwana wersja, co oznacza, że interfejs API zostaje usunięty począwszy od określonej wersji.

      // 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();
      
    • Nieobsługiwana lista. 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ć [SupportedOSPlatform] atrybut z tą samą platformą, ale wyższą wersję, co oznacza, że interfejs API jest obsługiwany, począwszy od tej wersji.

      // 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();
      
    • Niespójna lista. Jeśli najniższa wersja dla niektórych platform jest [SupportedOSPlatform], podczas gdy dla innych platform jest [UnsupportedOSPlatform], uważa się to za niespójną, której analizator nie obsługuje. W przypadku wystąpienia niespójności analizator ignoruje platformy [UnsupportedOSPlatform] .

      • Jeśli najniższe wersje atrybutów [SupportedOSPlatform] i [UnsupportedOSPlatform] są równe, analizator traktuje platformę jako część listy Obsługiwane tylko.
  • Atrybuty platformy można stosować do typów, elementów członkowskich (metod, pól, właściwości i zdarzeń) i zestawów z różnymi nazwami lub wersjami platformy.

    • Atrybuty zastosowane na najwyższym poziomie target mają wpływ na wszystkie jego elementy składowe i typy.
    • Atrybuty na poziomie podrzędnym mają zastosowanie tylko wtedy, gdy są one zgodne z regułą "adnotacje podrzędne mogą zawęzić obsługę platform, ale nie mogą ją poszerzyć".
      • Jeśli element nadrzędny posiada listę Tylko obsługiwane, atrybuty elementu podrzędnego nie mogą dodać nowego wsparcia dla platformy, ponieważ oznaczałoby to rozszerzenie wsparcia dla elementu nadrzędnego. Obsługę nowej platformy można dodać tylko do samego elementu nadrzędnego. Jednak element podrzędny może mieć atrybut Supported dla tej samej platformy, ale tylko w nowszych wersjach, co zawęża obsługę. Ponadto element podrzędny może mieć atrybut Unsupported na tej samej platformie, co również ogranicza wsparcie dla elementu nadrzędnego.
      • Jeśli element nadrzędny ma listę tylko z nieobsługiwanymi elementami, atrybuty członków podrzędnych mogą dodać obsługę dla nowej platformy, co zawęża zakres obsługi przez element nadrzędny. Nie może jednak mieć atrybutu Supported dla tej samej platformy co nadrzędna, ponieważ rozszerza ona obsługę nadrzędną. Obsługę tej samej platformy można dodać tylko do elementu nadrzędnego, w którym zastosowano oryginalny Unsupported atrybut.
    • Jeśli [SupportedOSPlatform("platformVersion")] zastosowano więcej niż raz dla interfejsu API o tej samej platform nazwie, analizator uwzględnia tylko jedną z minimalną wersją.
    • Jeśli [UnsupportedOSPlatform("platformVersion")] zastosowano więcej niż dwa razy dla interfejsu API o tej samej platform nazwie, analizator uwzględnia tylko te dwa z najwcześniejszymi wersjami.

    Uwaga / Notatka

    Interfejs API, który początkowo był obsługiwany, ale nieobsługiwany (usunięty) w nowszej wersji nie powinien zostać ponownie obsługiwany w jeszcze nowszej wersji.

Przykłady działania atrybutów i ich diagnostyki

// 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();
}

Obsługa zgłoszonych ostrzeżeń

Zalecanym sposobem radzenia sobie z tymi diagnostykami jest upewnienie się, że podczas uruchamiania na odpowiedniej platformie są wywoływane tylko interfejsy API specyficzne dla platformy. Poniżej przedstawiono opcje, których można użyć, aby rozwiązać problem z ostrzeżeniami. wybierz niezależnie od tego, która z nich jest najbardziej odpowiednia dla Twojej sytuacji:

  • Pilnuj połączenia. Można to osiągnąć, warunkowo wywołując kod w czasie wykonywania. Sprawdź, czy działasz na żądanej Platform, korzystając z jednej z metod sprawdzania platformy, na przykład OperatingSystem.Is<Platform>() lub OperatingSystem.Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0). Przykład.

  • Oznacz miejsce wywołania jako specyficzne dla platformy. Możesz również oznaczyć własne interfejsy API jako specyficzne dla platformy, co skutecznie przekazuje wymagania osobom wywołującym. Oznacz metodę, typ lub cały zbiór (assembly), w którym się znajduje, tymi samymi atrybutami co wywołanie zależne od platformy. Przykłady.

  • Potwierdzanie lokacji wywołania za pomocą sprawdzania platformy. Jeśli nie chcesz narzutu dodatkowej if instrukcji w czasie wykonywania, użyj polecenia Debug.Assert(Boolean). Przykład.

  • Usuń kod. Ogólnie nie jest to, czego potrzebujesz, ponieważ oznacza to utratę wierności, gdy kod jest używany przez użytkowników systemu Windows. W przypadkach, gdy istnieje alternatywa międzyplatformowa, prawdopodobnie lepiej jest używać jej zamiast interfejsów API specyficznych dla platformy.

  • Pomiń ostrzeżenie. Możesz również po prostu pominąć ostrzeżenie za pomocą wpisu EditorConfig lub #pragma warning disable CA1416. Jednak ta opcja powinna być ostateczna w przypadku korzystania z interfejsów API specyficznych dla platformy.

    Wskazówka

    Podczas wyłączania ostrzeżeń za pomocą dyrektyw prekompilatora #pragma, identyfikatory, które są celem, są wrażliwe na wielkość liter. Na przykład ca1416 nie wyłączy ostrzeżenie CA1416.

Chroń dedykowane dla platformy interfejsy API za pomocą metod zabezpieczania

Nazwa platformy metody guard powinna być zgodna z nazwą platformy interfejsu API zależną od platformy wywołującej. Jeśli ciąg platformy wywołującego interfejsu API zawiera wersję:

  • Dla atrybutu [SupportedOSPlatform("platformVersion")] platforma version metody guard powinna być większa lub równa platformie wywołującej Version.

  • Dla atrybutu [UnsupportedOSPlatform("platformVersion")] platforma version metody guard powinna być mniejsza lub równa platformie wywołującej Version.

    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
        }
    }
    
  • Jeśli musisz chronić kod przeznaczony dla celów netstandard lub netcoreapp, gdzie nowe API OperatingSystem są niedostępne, API RuntimeInformation.IsOSPlatform może być używany i będzie uwzględniany przez analizator. Nie jest to jednak tak zoptymalizowane, jak nowe interfejsy API dodane w systemie OperatingSystem. Jeśli platforma nie jest obsługiwana w strukturze OSPlatform, możesz wywołać OSPlatform.Create(String) i przekazać nazwę platformy, która jest również rozpoznawana przez analizator.

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

Dodawanie adnotacji do interfejsów API z atrybutami ochrony platformy i używanie ich jako niestandardowej ochrony

Jak pokazano wcześniej, analizator rozpoznaje metody statyczne ochrony platformy w typie OperatingSystem , takim jak OperatingSystem.IsWindows, a także RuntimeInformation.IsOSPlatform. Możesz jednak buforować wynik ochrony i ponownie użyć go lub użyć niestandardowych metod ochrony do sprawdzania platformy. Analizator musi rozpoznawać takie interfejsy API jako ochronę niestandardową i nie powinien ostrzegać przed interfejsami API chronionymi przez nie. Atrybuty ochrony zostały wprowadzone na platformie .NET 6 w celu obsługi tego scenariusza:

  • SupportedOSPlatformGuardAttribute adnotuje interfejsy API, których można używać jako ochrony dla interfejsów API z adnotacjami SupportedOSPlatformAttribute.
  • UnsupportedOSPlatformGuardAttribute adnotuje interfejsy API, których można używać jako ochrony dla interfejsów API z adnotacjami UnsupportedOSPlatformAttribute.

Te atrybuty mogą opcjonalnie zawierać numer wersji. Można je stosować wiele razy, aby chronić więcej niż jedną platformę i może służyć do dodawania adnotacji do pola, właściwości lub metody.

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

Oznacz punkt wywołania jako specyficzny dla platformy

Nazwy platform powinny być zgodne z wywołującym interfejsem API zależnym od platformy. Jeśli ciąg platformy zawiera wersję:

  • W przypadku atrybutu [SupportedOSPlatform("platformVersion")] platforma version miejsca wywołania powinna być większa lub równa platformie wywołującej Version

  • Dla atrybutu [UnsupportedOSPlatform("platformVersion")] platforma miejsca wywołania version powinna być mniejsza lub równa platformie wywołującej Version

    // 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();
    }
    

Potwierdzenie miejsca wywołania z kontrolą platformy

Wszystkie kontrole warunkowe używane w przykładach funkcji Platform Guard mogą być również używane jako warunek .Debug.Assert(Boolean)

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

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

    LinuxOnlyApi(); // will not warn
}

Zobacz także