Analizator zgodności platformy

Prawdopodobnie znasz motto "One .NET": jedną, ujednoliconą platformę do tworzenia dowolnego typu aplikacji. Zestaw SDK platformy .NET 5 obejmuje ASP.NET Core, Entity Framework Core, WinForms, WPF, Xamarin i ML.NET oraz doda obsługę większej liczby platform w czasie. Platforma .NET 5 stara się zapewnić środowisko, w którym nie trzeba wnioskować o różnych odmianach platformy .NET, ale nie próbuje w pełni wyodrębnić bazowego systemu operacyjnego . Nadal będziesz mieć możliwość wywoływania interfejsów API specyficznych dla platformy, na przykład P/Invokes, WinRT lub powiązań platformy Xamarin dla systemów iOS i Android.

Jednak użycie interfejsów API specyficznych dla platformy w składniku oznacza, że kod nie działa już na wszystkich platformach. Potrzebowaliśmy sposobu wykrywania tego w czasie projektowania, aby deweloperzy uzyskiwali diagnostykę, gdy przypadkowo używają interfejsów API specyficznych dla platformy. Aby osiągnąć ten cel, platforma .NET 5 wprowadza analizator zgodności platformy i uzupełniające interfejsy API, aby ułatwić deweloperom identyfikowanie i używanie interfejsów API specyficznych dla platformy, jeśli jest to konieczne.

Nowe 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 w System.OperatingSystem klasie do bezpiecznego wywoływania interfejsów API specyficznych dla platformy. Na przykład OperatingSystem.IsWindows() można użyć funkcji do ochrony wywołania interfejsu API specyficznego dla systemu Windows, a OperatingSystem.IsWindowsVersionAtLeast() można użyć do ochrony wywołania interfejsu API specyficznego dla systemu Windows w wersji. Zobacz te przykłady użycia tych metod jako strażników odwołań interfejsu API specyficznych dla platformy.

Wymagania wstępne

Analizator zgodności platformy jest jednym z analizatorów jakości kodu Roslyn. Począwszy od platformy .NET 5, te analizatory są dołączone do zestawu .NET SDK. Analizator zgodności platformy jest domyślnie włączony tylko dla projektów docelowych net5.0 lub nowszych wersji. Można go jednak włączyć dla projektów przeznaczonych dla innych platform.

Jak analizator określa zależność platformy

  • Nieprzydatowany interfejs API jest uznawany za pracę na wszystkich platformach systemu operacyjnego.

  • Interfejs API oznaczony jako element [SupportedOSPlatform("platform")] jest uznawany za przenośny tylko do określonej platformy i wszystkich platform, z których jest on 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, że platforma nadzbioru jest również obsługiwana. Na przykład oznacza to, [SupportedOSPlatform("iOS")] że interfejs API jest obsługiwany na platformie iOS superzbioru MacCatalyst, a także na platformie .
    • 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 , czy 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> przeznaczonych dla projektu, a generowanie plików AssemblyInfo.cs jest włączone dla projektu).
      • Nie ostrzega , czy wywołanie interfejsu API specyficzne dla platformy jest chronione przy użyciu odpowiednich metod sprawdzania platformy (na przykład wywołania interfejsu API specyficznego dla systemu Windows chronionego przez OperatingSystem.IsWindows()usługę ).
      • Nie ostrzega , czy interfejs API specyficzny dla platformy jest przywołyany z tego samego kontekstu specyficznego dla platformy (lokacja wywołania ma również przypisaną wartość[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 platforma nadzbioru jest również nieobsługiwana. Na przykład oznacza, [UnsupportedOSPlatform("iOS")] że interfejs API jest nieobsługiwany na platformie iOS , a także na jej platformie nadzbioru . MacCatalyst
    • Analizator generuje ostrzeżenie tylko wtedy, gdy platform element jest skuteczny dla lokacji wywołania:
      • Ostrzega, czy projekt jest przeznaczony dla platformy, która jest przypisana jako nieobsługiwana (na przykład jeśli interfejs API jest przypisywany za pomocą [UnsupportedOSPlatform("windows")] elementu , a obiekt docelowy <TargetFramework>net5.0-windows</TargetFramework>witryny wywołania ).

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

        <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 build części 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. Jeśli na przykład serwer TFM to net5.0-windows10.0.19041, program MSBuild wprowadza [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] atrybut do pliku AssemblyInfo.cs , a cały zestaw jest uznawany za tylko 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 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.

Dołączanie platformy

Platforma .NET 6 wprowadza 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 nadzbioru. Jeśli metoda sprawdzania platformy w typie OperatingSystem ma SupportedOSPlatformGuard("supersetPlatform")] atrybut, supersetPlatform jest uważany za nadzbiór platformy systemu operacyjnego, dla którego sprawdza się metoda.

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

  • 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 ✔️ ✔️ ✔️ ✔️

Napiwek

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ę minimalnej obsługiwanej wersji, co oznacza, że interfejs API jest usuwany 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] w przypadku [UnsupportedOSPlatform] innych platform, jest uważana za niespójną, która nie jest obsługiwana dla analizatora. 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 członkowskie 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 ma listę Tylko obsługiwane, atrybuty elementu członkowskiego podrzędnego nie mogą dodać nowej obsługi platformy, ponieważ byłoby to rozszerzanie obsługi nadrzędnej. Obsługę nowej platformy można dodać tylko do samego elementu nadrzędnego. Jednak element podrzędny może mieć Supported atrybut dla tej samej platformy z nowszymi wersjami, co zawęża obsługę. Ponadto element podrzędny może mieć Unsupported atrybut z tą samą platformą, co również zawęża obsługę nadrzędną.
      • Jeśli element nadrzędny ma tylko nieobsługiwaną listę, atrybuty elementów członkowskich podrzędnych mogą dodać obsługę nowej platformy, ponieważ zawęża obsługę nadrzędną. 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

    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 używasz żądanej Platform metody, 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 witrynę wywołania jako specyficzną dla platformy. Możesz również oznaczyć własne interfejsy API jako specyficzne dla platformy, dzięki czemu wystarczy przekazać wymagania do osób wywołujących. Oznacz zawierającą metodę lub typ lub cały zestaw z 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 przypadku, gdy istnieje alternatywa dla wielu platform, prawdopodobnie lepiej jest używać tych interfejsów API dla określonych platform.

  • 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.

    Napiwek

    Podczas wyłączania ostrzeżeń przy użyciu #pragma dyrektyw prekompilatora identyfikatory, których dotyczysz, są uwzględniane wielkość liter. Na przykład ca1416 nie wyłączy ostrzeżenie CA1416.

Ochrona interfejsów API specyficznych dla platformy z metodami ochrony

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

  • SupportedOSPlatformGuardAttributedodawać adnotacje do interfejsów API, których można używać jako ochrony dla interfejsów API z adnotacjami .SupportedOSPlatformAttribute
  • UnsupportedOSPlatformGuardAttributedodawać adnotacje do interfejsów 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 witrynę wywołania jako specyficzną 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 lokacji wywołania powinna być większa lub równa platformie wywołującej Version

  • Dla atrybutu [UnsupportedOSPlatform("platformVersion")] platforma version lokacji wywołania 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();
    }
    

Potwierdzanie wywołania lokacji za pomocą sprawdzania 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 też