Analyseur de compatibilité de plateforme

Vous avez probablement déjà entendu le slogan : « Un seul .NET » : une plateforme unique et unifiée pour créer n’importe quel type d’application. Le SDK .NET 5 inclut ASP.NET Core, Entity Framework Core, WinForms, WPF, Xamarin et ML.NET, et offrira une prise en charge d’autres plateformes dans le futur. L’objectif de .NET 5 est de fournir une expérience où vous n’avez pas à vous préoccuper des différentes versions de .NET, sans pour cela faire totalement abstraction du système d’exploitation sous-jacent. Vous pourrez toujours appeler des API propres à la plateforme, comme P/Invokes, WinRT, ou les liaisons Xamarin pour iOS et Android.

Cependant, l’utilisation d’API propres à la plateforme sur un composant fait que le code ne fonctionne plus sur toutes les plateformes. Nous voulions trouver un moyen de détecter cela au moment de la conception et de fournir aux développeurs des diagnostics quand ils utilisent par inadvertance des API propres à la plateforme. Dans ce but, nous avons introduit l’analyseur de compatibilité de plateforme et des API complémentaires dans NET 5 afin d’aider les développeurs à identifier et à utiliser les API propres à la plateforme de façon appropriée.

Les nouvelles API sont les suivantes :

  • SupportedOSPlatformAttribute pour annoter les API comme étant propres à la plateforme et UnsupportedOSPlatformAttribute pour annoter les API comme étant non prises en charge sur tel ou tel système d’exploitation. Ces attributs peuvent éventuellement inclure le numéro de version, et ont déjà été appliqués à certaines API propres à la plateforme dans les bibliothèques .NET principales.
  • Les méthodes statiques Is<Platform>() et Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) de la classe System.OperatingSystem pour appeler de manière sécurisée des API propres à la plateforme. Par exemple, OperatingSystem.IsWindows() peut être utilisé pour protéger un appel à une API Windows, et OperatingSystem.IsWindowsVersionAtLeast() peut être utilisé pour protéger un appel à une API Windows d’une version spécifique. Consultez ces exemples qui montrent comment utiliser ces méthodes pour protéger les références aux API propres à la plateforme.

Prérequis

L’analyseur de compatibilité de plateforme est l’un des analyseurs Roslyn permettant de vérifier la qualité du code. À compter de .NET 5, ces analyseurs sont inclus dans le SDK .NET. L’analyseur de compatibilité de plateforme est activé par défaut uniquement pour les projets qui ciblent net5.0 ou une version ultérieure. Vous pouvez toutefois l’activer pour des projets qui ciblent d’autres frameworks.

Comment l’analyseur détermine la dépendance à la plateforme

  • Une API non attribuée est supposée fonctionner sur toutes les plateformes de système d’exploitation.

  • Une API marquée avec [SupportedOSPlatform("platform")] est considérée comme étant portable uniquement sur la plateforme spécifiée et toutes les plateformes dont elle est un sous-ensemble.

    • L’attribut peut être appliqué plusieurs fois pour indiquer une prise en charge de plusieurs plateformes, par exemple [SupportedOSPlatform("windows"), SupportedOSPlatform("Android29.0")].
    • Si la plateforme est un sous-ensemble d’une autre plateforme, l’attribut implique que la plateforme de surensemble est également prise en charge. Par exemple, [SupportedOSPlatform("iOS")] implique que l’API est prise en charge sur iOS et aussi sur sa plateforme de surensemble, MacCatalyst.
    • L’analyseur génère un avertissement si des API propres à la plateforme sont référencées sans un contexte de plateforme approprié :
      • Avertit si le projet ne cible pas la plateforme prise en charge (par exemple, quand une API Windows est appelée à partir d’un projet ciblant iOS <TargetFramework>net5.0-ios14.0</TargetFramework>).
      • Avertit si le projet est multiplateforme et qu’il appelle des API propres à la plateforme (par exemple, quand une API Windows est appelée à partir du TFM multiplateforme <TargetFramework>net5.0</TargetFramework>).
      • N’avertit pas si l’API propre à la plateforme est référencée dans un projet qui cible l’une des plateformes spécifiées (par exemple, quand une API Windows est appelée à partir d’un projet ciblant Windows <TargetFramework>net5.0-windows</TargetFramework> et que la génération du fichier AssemblyInfo.cs est activée pour le projet).
      • N’avertit pas si l’appel de l’API propre à la plateforme est protégé par les méthodes de vérification de plateforme correspondantes (par exemple, quand l’appel d’une API Windows est protégé par OperatingSystem.IsWindows()).
      • N’avertit pas si l’API propre à la plateforme est référencée à partir du même contexte de plateforme (site d’appel également attribué avec [SupportedOSPlatform("platform")).
  • Une API marquée avec [UnsupportedOSPlatform("platform")] est considérée comme non prise en charge sur la plateforme spécifiée et toutes les plateformes dont elle est un sous-ensemble, mais prise en charge sur toutes les autres plateformes.

    • L’attribut peut être appliqué plusieurs fois avec différentes plateformes, par exemple, [UnsupportedOSPlatform("iOS"), UnsupportedOSPlatform("Android29.0")].
    • Si la plateforme est un sous-ensemble d’une autre plateforme, l’attribut implique que la plateforme de surensemble n’est pas prise en charge non plus. Par exemple, [UnsupportedOSPlatform("iOS")] implique que l’API n’est prise en charge ni sur iOS ni sur sa plateforme de surensemble, MacCatalyst.
    • L’analyseur génère un avertissement uniquement si platform est appliqué pour le site d’appel :
      • Avertit si le projet cible la plateforme qui est attribuée comme non prise en charge (par exemple, si l’API est attribuée avec [UnsupportedOSPlatform("windows")] et que le site d’appel cible <TargetFramework>net5.0-windows</TargetFramework>).

      • Avertit si le projet cible plusieurs plateformes et que platform est inclus dans le groupe d’éléments MSBuild <SupportedPlatform> par défaut, ou si platform est ajouté manuellement dans le groupe d’éléments MSBuild<SupportedPlatform> :

        <ItemGroup>
            <SupportedPlatform Include="platform" />
        </ItemGroup>
        
      • N’avertit pas si vous créez une application qui ne cible pas la plateforme non prise en charge ou qui cible plusieurs plateformes mais que la plateforme n’est pas incluse dans le groupe d’éléments MSBuild <SupportedPlatform> par défaut.

  • Les deux attributs peuvent être instanciés avec ou sans numéros de version dans le nom de plateforme. Les numéros de version suivent le format major.minor[.build[.revision]] ; major.minor est obligatoire, et les éléments build et revision sont facultatifs. Par exemple, « Windows6.1 » désigne Windows version 6.1, mais « Windows » est interprété comme Windows 0.0.

Pour plus d’informations, consultez Exemples d’utilisation des attributs et de diagnostics générés.

Comment l’analyseur reconnaît les plateformes cibles du TFM

L’analyseur ne vérifie pas les plateformes cibles du moniker de framework cible (TFM) à partir des propriétés MSBuild, telles que <TargetFramework> ou <TargetFrameworks>. Si le TFM a une plateforme cible, MSBuild injecte un attribut SupportedOSPlatform avec le nom de la plateforme ciblée dans le fichier AssemblyInfo.cs, qui est utilisé par l’analyseur. Par exemple, si le TFM est net5.0-windows10.0.19041, MSBuild injecte l’attribut [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] dans le fichier AssemblyInfo.cs, et l’assembly entier est considéré comme étant « Windows uniquement ». Par conséquent, l’appel d’API « Windows uniquement » avec une version 7.0 ou antérieure ne générerait pas d’avertissements dans le projet.

Notes

Si la génération du fichier AssemblyInfo.cs est désactivée pour le projet (autrement dit, la propriété <GenerateAssemblyInfo> a la valeur false), l’attribut SupportedOSPlatform de niveau assembly requis ne peut pas être ajouté par MSBuild. Dans ce cas, vous pouvez voir des avertissements pour une utilisation d’API propres à la plateforme, même si vous ciblez cette plateforme. Pour résoudre les avertissements, activez la génération du fichier AssemblyInfo.cs ou ajoutez l’attribut manuellement dans votre projet.

Inclusion de plateforme

.NET 6 introduit le concept d’inclusion de plateforme, où une plateforme peut être un sous-ensemble d’une autre plateforme. Une annotation pour la plateforme de sous-ensemble implique la même prise en charge (ou son absence) pour la plateforme de surensemble. Si une méthode de vérification de plateforme dans le type OperatingSystem a un attribut SupportedOSPlatformGuard("supersetPlatform")], supersetPlatform est considéré comme un surensemble de la plateforme de système d’exploitation que la méthode vérifie.

Par exemple, la méthode OperatingSystem.IsIOS() a l’attribut [SupportedOSPlatformGuard("MacCatalyst")]. Cela signifie que les instructions suivantes s’appliquent :

  • Les méthodes OperatingSystem.IsIOS() et OperatingSystem.IsIOSVersionAtLeast vérifient non seulement la plateforme iOS, mais aussi la plateforme MacCatalyst.
  • [SupportedOSPlatform("iOS")] implique que l’API est prise en charge sur iOS et aussi sur sa plateforme de surensemble, MacCatalyst. Vous pouvez utiliser l’attribut [UnsupportedOSPlatform("MacCatalyst")] pour exclure cette prise en charge implicite.
  • [UnsupportedOSPlatform("iOS") implique que l’API n’est pas prise en charge sur iOS et MacCatalyst. Vous pouvez utiliser l’attribut [SupportedOSPlatform("MacCatalyst")] pour exclure cette absence de prise en charge implicite.

Examinez la matrice de couverture suivante, où ✔️ indique que la plateforme est prise en charge et où ❌ indique que la plateforme n’est pas prise en charge.

Plate-forme SupportedOSPlatform(subset) SupportedOSPlatform(superset) UnsupportedOSPlatform(subset) UnsupportedOSPlatform(superset)
Sous-ensemble ✔️ ✔️
Surensemble ✔️ ✔️ ✔️ ✔️

Conseil

Les mêmes règles s’appliquent pour les attributs SupportedOSPlatformGuard et UnsupportedOSPlatformGuard.

L’extrait de code suivant montre comment combiner des attributs pour définir le bon niveau de prise en charge.

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

Scénarios avancés pour les combinaisons d’attributs

  • Si une combinaison d’attributs [SupportedOSPlatform] et [UnsupportedOSPlatform] est présente, tous les attributs sont regroupés par identificateur de plateforme de système d’exploitation :

    • Liste Prises en charge uniquement. Si la version la plus basse pour chaque plateforme de système d’exploitation est un attribut [SupportedOSPlatform], l’API est considérée comme prise en charge uniquement par les plateformes listées et non prise en charge par toutes les autres plateformes. Les attributs [UnsupportedOSPlatform] facultatifs pour chaque plateforme peuvent uniquement avoir une version supérieure à la version minimale prise en charge, ce qui indique que l’API est supprimée à partir de la version spécifiée.

      // API is only supported on Windows from version 6.2 to 10.0.19041.0 and all versions of Linux
      // The API is considered not supported for all other platforms.
      [SupportedOSPlatform("windows6.2")]
      [UnsupportedOSPlatform("windows10.0.19041.0")]
      [SupportedOSPlatform("linux")]
      public void ApiSupportedFromWindows80SupportFromCertainVersion();
      
    • Liste Non prises en charge uniquement. Si la version la plus basse pour chaque plateforme de système d’exploitation est un attribut [UnsupportedOSPlatform], l’API est considérée comme non prise en charge uniquement par les plateformes listées, et prise en charge par toutes les autres plateformes. La liste peut avoir un attribut [SupportedOSPlatform] avec la même plateforme, mais avec une version supérieure, ce qui indique que l’API est prise en charge à compter de cette version.

      // 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();
      
    • Liste Incohérentes. Si la version la plus basse pour certaines plateformes est [SupportedOSPlatform], mais [UnsupportedOSPlatform] pour d’autres plateformes, elle est considérée comme incohérente, ce qui n’est pas pris en charge pour l’analyseur. En cas d’incohérence, l’analyseur ignore les plateformes [UnsupportedOSPlatform].

      • Si les versions les plus basses des attributs [SupportedOSPlatform] et [UnsupportedOSPlatform] sont identiques, l’analyseur considère que la plateforme fait partie de la liste Prises en charge uniquement.
  • Les attributs de plateforme peuvent être appliqués à des types, des membres (méthodes, champs, propriétés et événements) et des assemblys ayant des noms ou des versions de plateforme différents.

    • Les attributs appliqués au premier niveau target impactent tous ses membres et types.
    • Les attributs de niveau enfant s’appliquent uniquement s’ils respectent la règle « les annotations des enfants peuvent restreindre la prise en charge des plateformes, mais elles ne peuvent pas l’étendre ».
      • Lorsque le parent a la liste Prises en charge uniquement, les attributs des membres enfants ne peuvent pas ajouter de prise en charge d’une nouvelle plateforme, car cela étendrait la prise en charge du parent. La prise en charge d’une nouvelle plateforme ne peut être ajoutée qu’au parent lui-même. Mais l’enfant peut avoir l’attribut Supported pour la même plateforme avec des versions ultérieures, car cela réduit la prise en charge. En outre, l’enfant peut avoir l’attribut Unsupported avec la même plateforme, car cela restreint également la prise en charge du parent.
      • Lorsque le parent a la liste Non prises en charge uniquement, les attributs des membres enfants peuvent ajouter la prise en charge d’une nouvelle plateforme, car cela restreint la prise en charge du parent. Mais il ne peut pas avoir l’attribut Supported pour la même plateforme que le parent, car cela étend la prise en charge du parent. La prise en charge de la même plateforme peut uniquement être ajoutée au parent où l’attribut Unsupported initial a été appliqué.
    • Si l’attribut [SupportedOSPlatform("platformVersion")] est appliqué plusieurs fois pour une API portant le même nom platform, l’analyseur prend uniquement en compte l’occurrence avec la version la plus basse.
    • Si l’attribut [UnsupportedOSPlatform("platformVersion")] est appliqué plus de deux fois pour une API portant le même nom platform, l’analyseur prend uniquement en compte les deux occurrences avec les versions les plus anciennes.

    Notes

    Une API qui était prise en charge initialement, mais qui n’est pas prise en charge (car supprimée) dans une version ultérieure n’est pas censée être de nouveau prise en charge dans une prochaine version.

Exemples d’utilisation des attributs et de diagnostics générés

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

Traiter les avertissements signalés

La méthode recommandée pour traiter ces diagnostics est de vérifier que vous appelez uniquement des API propres à la plateforme lors de l’exécution sur une plateforme appropriée. Parmi les options suivantes que vous pouvez utiliser pour traiter les avertissements, choisissez celle la plus adaptée à votre situation :

  • Protéger l’appel. Pour ce faire, appelez le code de manière conditionnelle au moment de l’exécution. Vérifiez si l’environnement d’exécution Platform est celui souhaité en utilisant l’une des méthodes de vérification de la plateforme, par exemple OperatingSystem.Is<Platform>() ou OperatingSystem.Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0). Exemple.

  • Marquer le site d’appel comme étant propre à la plateforme. Vous pouvez aussi choisir de marquer vos propres API comme étant propres à la plateforme, ce qui a pour conséquence de seulement transférer les exigences à vos appelants. Marquez la méthode ou le type contenant ou l’assembly entier avec les mêmes attributs que l’appel dépendant de la plateforme référencée. Exemples.

  • Valider le site d’appel avec la vérification de plateforme. Si vous ne souhaitez pas la surcharge d’une instruction if supplémentaire au moment de l’exécution, utilisez Debug.Assert(Boolean). Exemple.

  • Supprimer le code. En général, ce n’est pas ce que vous voudrez faire, car cela entraîne la perte de la fidélité lorsque votre code est utilisé par les utilisateurs de Windows. Quand il y a une alternative multiplateforme, il est souvent préférable d’utiliser cette option plutôt que des API propres à la plateforme.

  • Supprimer l’avertissement. Vous pouvez aussi simplement supprimer l’avertissement, via une entrée EditorConfig ou #pragma warning disable CA1416. Toutefois, cette option doit être choisie en dernier recours quand vous utilisez des API propres à la plateforme.

    Conseil

    Si les avertissements sont désactivés avec les directives de précompilateur #pragma, les identificateurs que vous ciblez respectent la casse. Par exemple, ca1416 ne désactive pas réellement l’avertissement CA1416.

Protéger les API propres à la plateforme avec des méthodes de protection

Le nom de plateforme de la méthode de protection doit correspondre au nom de plateforme de l’API d’appel propre à la plateforme. Si la chaîne de plateforme de l’API d’appel inclut la version :

  • Pour l’attribut [SupportedOSPlatform("platformVersion")], la version de la plateforme de la méthode de protection doit être supérieure ou égale à la Versionde la plateforme d’appel.

  • Pour l’attribut [UnsupportedOSPlatform("platformVersion")], la version de la plateforme de la méthode de protection doit être inférieure ou égale à la Version de la plateforme d’appel.

    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
        }
    }
    
  • Si vous devez protéger le code qui cible netstandard ou netcoreapp où les nouvelles API OperatingSystem ne sont pas disponibles, l’API RuntimeInformation.IsOSPlatform peut être utilisée et sera respectée par l’analyseur. Cette API n’est toutefois pas autant optimisée que les nouvelles API ajoutées dans OperatingSystem. Si la plateforme n’est pas prise en charge dans le struct OSPlatform, vous pouvez appeler OSPlatform.Create(String) et passer le nom de la plateforme, ce que l’analyseur respecte également.

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

Annoter les API avec des attributs de protection de plateforme en vue de les utiliser comme protection personnalisée

Comme expliqué précédemment, l’analyseur reconnaît les méthodes statiques de protection de plateforme dans le type OperatingSystem, telles que OperatingSystem.IsWindows, et également RuntimeInformation.IsOSPlatform. Vous pouvez aussi mettre en cache le résultat de la protection dans un champ en vue de le réutiliser, ou utiliser des méthodes de protection personnalisée pour la vérification d’une plateforme. L’analyseur doit pouvoir reconnaître ces API comme étant une protection personnalisée et ne doit pas générer d’avertissements pour les API ainsi protégées. Les attributs de protection ont été introduits dans .NET 6 pour prendre en charge ce scénario :

  • SupportedOSPlatformGuardAttribute annote les API qui peuvent être utilisées comme protection pour les API annotées avec SupportedOSPlatformAttribute.
  • UnsupportedOSPlatformGuardAttribute annote les API qui peuvent être utilisées comme protection pour les API annotées avec UnsupportedOSPlatformAttribute.

Ces attributs peuvent éventuellement inclure un numéro de version. Ils peuvent être appliqués plusieurs fois pour protéger plusieurs plateformes, et être utilisés pour l’annotation d’un champ, d’une propriété ou d’une méthode.

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

Marquer le site d’appel comme étant propre à la plateforme

Les noms de plateforme doivent correspondre à l’API d’appel propre à la plateforme. Si la chaîne de plateforme inclut une version :

  • Pour l’attribut [SupportedOSPlatform("platformVersion")], la version de la plateforme du site d’appel doit être supérieure ou égale à la Version de la plateforme d’appel.

  • Pour l’attribut [UnsupportedOSPlatform("platformVersion")], la version de la plateforme du site d’appel doit être inférieure ou égale à la Version de la plateforme d’appel.

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

Valider le site d’appel avec la vérification de plateforme

Toutes les vérifications conditionnelles utilisées dans les exemples de protection de plateforme peuvent également être utilisées comme condition pour 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
}

Voir aussi