プラットフォーム互換性アナライザー

"One .NET" という標語をお聞きになったことがあるかもしれません。あらゆる種類のアプリケーションを構築するための、1 つの統一プラットフォームという意味です。 .NET 5 SDK には、ASP.NET Core、Entity Framework Core、WinForms、WPF、Xamarin、ML.NET が含まれています。また、時間の経過と共にさらなるプラットフォームのサポートが追加されます。 .NET 5 では、.NET のさまざまなフレーバーを判断する必要がないエクスペリエンスの提供を目指していますが、基になるオペレーティング システム (OS) を完全に抽象化しようとしているわけではありません。 ユーザーは引き続き、P/Invoke、WinRT、または iOS および Android 用の Xamarin バインドなど、プラットフォーム固有の API を呼び出すことができます。

ただし、コンポーネント上でプラットフォーム固有の API を使用すると、一部のプラットフォームでそのコードが動作しなくなります。 デザイン時にこれを検出して、開発者が誤ってプラットフォーム固有の API を使用したときに診断を取得できるようにするための方法が必要でした。 この目標を達成するために、.NET 5 ではプラットフォーム互換性アナライザーと、開発者がプラットフォーム固有の API を特定して適切な場所で使用できるようにするための、補完的な API が導入されます。

新しい API には以下が含まれます。

  • API にプラットフォーム固有という注釈を付けるための SupportedOSPlatformAttribute と、API に特定の OS でサポートされていないという注釈を付けるための UnsupportedOSPlatformAttribute。 これらの属性には必要に応じてバージョン番号を含めることができます。コア .NET ライブラリに含まれている一部のプラットフォーム固有の API には既に適用されています。
  • プラットフォーム固有の API を安全に呼び出すための、System.OperatingSystem クラスの Is<Platform>() および Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) 静的メソッド。 たとえば、OperatingSystem.IsWindows() を使用して Windows 固有の API 呼び出しを保護したり、OperatingSystem.IsWindowsVersionAtLeast() を使用してバージョン付きの Windows 固有の API 呼び出しを保護したりできます。 これらのメソッドを使用してプラットフォーム固有の API 参照を保護する方法については、以下のをご覧ください。

[前提条件]

プラットフォーム互換性アナライザーは、Roslyn コード品質アナライザーの 1 つです。 .NET 5 以降、これらのアナライザーは .NET SDK に含まれるようになりました。 プラットフォーム互換性アナライザーは、net5.0 以降のバージョンをターゲットとするプロジェクトに対してのみ既定で有効になります。 ただし、他のフレームワークをターゲットとするプロジェクトに対しても有効にすることができます。

アナライザーによってプラットフォームの依存関係が判断されるしくみ

  • 属性なしの API全 OS プラットフォームで動作すると見なされます。

  • [SupportedOSPlatform("platform")] でマークされた API は、指定されたプラットフォームと、それがサブセットであるすべてのプラットフォームにのみ移植可能と見なされます。

    • この属性を複数回適用して、複数プラットフォームのサポート を示すことができます (例: [SupportedOSPlatform("windows"), SupportedOSPlatform("Android29.0")])。
    • プラットフォームが別のプラットフォームのサブセットである場合、この属性は、スーパーセット プラットフォームもサポートされることを示唆します。 たとえば、[SupportedOSPlatform("iOS")] は、API が iOS でサポートされ、そのスーパーセット プラットフォーム MacCatalyst でもサポートされていることを意味します。
    • 適切なプラットフォーム コンテキストを使用せずにプラットフォーム固有の API が参照されている場合は、アナライザーによって警告が生成されます。
      • プロジェクトがサポート対象プラットフォームをターゲットとしていない場合、警告が表示されます (たとえば、iOS <TargetFramework>net5.0-ios14.0</TargetFramework> をターゲットとしているプロジェクトから呼び出された Windows 固有の API)。
      • プロジェクトがクロスプラットフォームで、プラットフォーム固有の API を呼び出している場合、警告が表示されます (たとえば、クロスプラットフォームの TFM <TargetFramework>net5.0</TargetFramework> から呼び出された Windows 固有の API)。
      • プラットフォーム固有の API が、指定されたプラットフォームのいずれかをターゲットとしているプロジェクト内で参照される場合、警告は表示されません (たとえば、Windows <TargetFramework>net5.0-windows</TargetFramework> をターゲットとするプロジェクトから呼び出された Windows 固有の API の場合、AssemblyInfo.cs ファイルの生成がプロジェクトに対して有効になります)。
      • プラットフォーム固有の API 呼び出しが、対応するプラットフォーム チェック メソッドによって保護されている場合、警告は表示されません (たとえば、OperatingSystem.IsWindows() によって保護された Windows 固有の API の呼び出し)。
      • プラットフォーム固有の API が、同じプラットフォーム固有のコンテキストから参照されている場合 (呼び出しサイトにも属性 [SupportedOSPlatform("platform") が設定されている場合)、警告は表示されません
  • [UnsupportedOSPlatform("platform")] でマークされた API は、指定されたプラットフォームと、それがサブセットであるすべてのプラットフォームではサポートされていないが、他のすべてのプラットフォームではサポートされていると見なされます。

    • この属性は、異なるプラットフォームで複数回適用できます (例: [UnsupportedOSPlatform("iOS"), UnsupportedOSPlatform("Android29.0")])。
    • プラットフォームが別のプラットフォームのサブセットである場合、この属性は、スーパーセット プラットフォームもサポートされないことを示唆します。 たとえば、[UnsupportedOSPlatform("iOS")] は、API が iOS でサポートされておらず、そのスーパーセット プラットフォームである MacCatalyst でもサポートされていないことを意味します。
    • アナライザーによって警告が生成されるのは、platform が呼び出しサイトに対して有効である場合のみです。
      • サポート対象外の属性が設定されているプラットフォームをプロジェクトがターゲットとしている場合、警告が表示されます (たとえば、API に属性 [UnsupportedOSPlatform("windows")] が設定されていて、呼び出しサイトが <TargetFramework>net5.0-windows</TargetFramework> をターゲットとしている場合)。

      • プロジェクトに複数のターゲットがあり、platform が既定の MSBuild <SupportedPlatform> 項目グループに含まれている場合、または platform が手動で MSBuild<SupportedPlatform> 項目グループ内に含まれている場合、警告が表示されます

        <ItemGroup>
            <SupportedPlatform Include="platform" />
        </ItemGroup>
        
      • サポート対象外のプラットフォームをターゲットとしていないアプリをビルドする場合、またはビルドするアプリに複数のターゲットがあり、platform が既定の MSBuild <SupportedPlatform> 項目グループに含まれていない場合、警告は表示されません

  • どちらの属性も、プラットフォーム名の一部としてバージョン番号を含めても、含めなくてもインスタンス化できます。 バージョン番号は major.minor[.build[.revision]] の形式です。major.minor は必須であり、buildrevision の部分は省略可能です。 たとえば、"Windows6.1" は Windows バージョン 6.1 を示しますが、"Windows" は Windows 0.0 として解釈されます。

詳細については、属性の動作方法とそれらによって発生する診断の例に関するセクションをご覧ください。

アナライザーが TFM ターゲット プラットフォームを認識する方法

アナライザーは、MSBuild プロパティから <TargetFramework><TargetFrameworks> などのターゲット フレームワーク モニカー (TFM) ターゲット プラットフォームをチェックしません。 TFM にターゲット プラットフォームがある場合、MSBuild はアナライザーが使用する AssemblyInfo.cs ファイルに、SupportedOSPlatform を使用してターゲット プラットフォーム名を挿入します。 たとえば、TFM が net5.0-windows10.0.19041 の場合、MSBuild は AssemblyInfo.cs ファイルに [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] 属性を挿入します。すると、アセンブリ全体が Windows 専用と見なされます。 そのため、7.0 以下のバージョンの Windows 専用 API が呼び出された場合、プロジェクトでは警告は表示されません。

注意

プロジェクトで AssemblyInfo.cs ファイルの生成が無効になっている場合 (つまり、<GenerateAssemblyInfo> プロパティが false に設定されている場合)、MSBuild は必要なアセンブリ レベル属性 SupportedOSPlatform を追加できません。 この場合、そのプラットフォームをターゲットとしているにもかかわらず、プラットフォーム固有の API の使用に関する警告が表示されることがあります。 この警告は、AssemblyInfo.cs ファイルの生成を有効にするか、自分のプロジェクトに属性を手動で追加することによって解決できます。

プラットフォーム包含

.NET 6 には "プラットフォーム包含" の概念が導入されていて、1 つのプラットフォームを別のプラットフォームのサブセットにすることができます。 サブセット プラットフォームに関する注釈は、スーパーセット プラットフォームに対する同じサポート (またはそれがないこと) を意味します。 OperatingSystem 型のプラットフォーム チェック メソッドに SupportedOSPlatformGuard("supersetPlatform")] 属性が使用されている場合、supersetPlatform は、そのメソッドでチェックする対象の OS プラットフォームのスーパーセットであると見なされます。

たとえば、OperatingSystem.IsIOS() メソッドには属性 [SupportedOSPlatformGuard("MacCatalyst")] が使用されています。 したがって、次のことが当てはまります。

  • OperatingSystem.IsIOS() および OperatingSystem.IsIOSVersionAtLeast メソッドを使用すると、iOS プラットフォームだけでなく MacCatalyst プラットフォームもチェックされます。
  • [SupportedOSPlatform("iOS")] は、API が iOS でサポートされ、そのスーパーセット プラットフォームである MacCatalyst でもサポートされていることを意味します。 [UnsupportedOSPlatform("MacCatalyst")] 属性を使用して、この暗黙のサポートを除外できます。
  • [UnsupportedOSPlatform("iOS") は、API が iOSMacCatalyst でサポートされていないことを意味します。 [SupportedOSPlatform("MacCatalyst")] 属性を使用して、この暗黙のサポート欠如を除外できます。

次のカバレッジ マトリックスについて考えてみます。✔️ はプラットフォームがサポートされていることを、❌ はプラットフォームがサポート "されていない" ことを示します。

プラットフォーム SupportedOSPlatform(subset) SupportedOSPlatform(superset) UnsupportedOSPlatform(subset) UnsupportedOSPlatform(superset)
サブセット ✔️ ✔️
スーパーセット ✔️ ✔️ ✔️ ✔️

ヒント

SupportedOSPlatformGuard および UnsupportedOSPlatformGuard 属性にも同じルールが適用されます。

次のコード スニペットは、属性を組み合わせて適切なサポート レベルを設定する方法を示しています。

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

属性の組み合わせの高度なシナリオ

  • [SupportedOSPlatform] 属性と [UnsupportedOSPlatform] 属性の組み合わせが存在する場合、すべての属性は OS プラットフォーム識別子によってグループ化されます。

    • サポート対象のみのリスト。 各 OS プラットフォームの最小バージョンが [SupportedOSPlatform] 属性である場合、その API はリストに含まれているプラットフォームでのみサポートされ、他のすべてのプラットフォームではサポートされないと見なされます。 各プラットフォームの省略可能な [UnsupportedOSPlatform] 属性には、サポートされる最小バージョンより上位のバージョンのみを設定できます。これは、指定されたバージョン以降で API が削除されることを示します。

      // 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();
      
    • サポート対象外のみのリスト。 各 OS プラットフォームの最小バージョンが [UnsupportedOSPlatform] 属性である場合、その API はリストに含まれているプラットフォームでのみサポートされず、他のすべてのプラットフォームではサポートされると見なされます。 リストには、同じプラットフォームのより上位のバージョンの [SupportedOSPlatform] 属性を含めることができます。これは、そのバージョン以降で API がサポートされることを示します。

      // 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();
      
    • 不整合なリスト。 あるプラットフォームの最小バージョンが [SupportedOSPlatform] であり、それが別のプラットフォームでは [UnsupportedOSPlatform] であった場合は、不整合と見なされます。これはアナライザーでサポートされません。 不整合が発生した場合、アナライザーは [UnsupportedOSPlatform] プラットフォームを無視します。

      • [SupportedOSPlatform] 属性と [UnsupportedOSPlatform] 属性の最小バージョンが等しい場合、アナライザーではそのプラットフォームがサポート対象のみのリストに含まれていると見なされます。
  • プラットフォーム属性は、型、メンバー (メソッド、フィールド、プロパティ、イベント)、およびプラットフォーム名やバージョンが異なるアセンブリに適用できます。

    • 最上位レベルの target に適用される属性は、そのすべてのメンバーと型に影響します。
    • 子レベルの属性が適用されるのは、それが "子注釈によってプラットフォームのサポートを絞り込むことはできるが、拡大することはできない" という規則に準拠している場合のみです。
      • 親がサポート対象のみのリストを持っている場合、子のメンバー属性で新しいプラットフォームのサポートを追加することはできません。親のサポートが拡大されるためです。 新しいプラットフォームのサポートは、親自体にのみ追加できます。 ただし、子が同じプラットフォームのそれ以降のバージョンに対して Supported 属性を持つことはできます。それによりサポートが絞り込まれるためです。 また、子が同じプラットフォームで Unsupported 属性を持つこともできます。親のサポートも絞り込まれるためです。
      • 親がサポート対象外のみのリストを持っている場合、親のサポートを絞り込むために、子のメンバー属性で新しいプラットフォームのサポートを追加することができます。 しかし、親と同じプラットフォームの Supported 属性を持つことはできません。親のサポートが拡張されるからです。 同じプラットフォームのサポートは、元の Unsupported 属性が適用された親に対してのみ追加できます。
    • 同じ platform 名の API に対して [SupportedOSPlatform("platformVersion")] が 2 回以上適用されている場合、最小バージョンのものだけがアナライザーによって考慮されます。
    • 同じ platform 名の API に対して [UnsupportedOSPlatform("platformVersion")] が 3 回以上適用されている場合、最も古いバージョンの 2 つだけがアナライザーによって考慮されます。

    注意

    最初はサポートされていたが後のバージョンではサポートされていない (削除された) API は、さらに後のバージョンで再びサポートされることはないと想定されます。

属性の動作方法とそれらによって生成される診断の例

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

報告された警告の処理

これらの診断に対処するための推奨される方法は、適切なプラットフォーム上で実行する場合にのみプラットフォーム固有の API を呼び出すようにすることです。 警告に対処するために使用できるオプションを以下に示します。ご自身の状況に最も適したものを選択してください。

  • 呼び出しを保護する。 これは、実行時にコードを条件付きで呼び出すことによって実現できます。 プラットフォーム チェック メソッド (OperatingSystem.Is<Platform>()OperatingSystem.Is<Platform>VersionAtLeast(int major, int minor = 0, int build = 0, int revision = 0) など) のいずれかを使用して、目的の Platform で実行しているかどうかを確認します。

  • 呼び出しサイトをプラットフォーム固有としてマークする。 独自の API をプラットフォーム固有としてマークすることもできます。これにより、実質的には要求を呼び出し元に転送するだけで済みます。 含まれているメソッドや型、またはアセンブリ全体を、参照されているプラットフォーム依存の呼び出しと同じ属性でマークします。

  • プラットフォーム チェックを使用して呼び出しサイトをアサートするif ステートメントを追加して実行時のオーバーヘッドを増やしたくない場合は、Debug.Assert(Boolean) を使用します。

  • コードを削除する。 通常は望ましい方法ではありません。Windows ユーザーがコードを使用する場合に忠実性が失われることを意味するためです。 クロスプラットフォームの代替手段が存在する場合は、プラットフォーム固有の API よりもそちらを使用した方がよい可能性があります。

  • 警告を抑制するEditorConfig エントリまたは #pragma warning disable CA1416 を使用して、単に警告を抑制することもできます。 ただし、プラットフォーム固有の API を使用する場合、このオプションは最後の手段にするべきです。

    ヒント

    #pragma プリコンパイラ ディレクティブを使用して警告を無効にすると、ターゲットとする識別子の大文字と小文字が区別されます。 たとえば、ca1416 は、実際には警告 CA1416 を無効にしません。

保護メソッドを使用してプラットフォーム固有の API を保護する

保護メソッドのプラットフォーム名は、呼び出し元のプラットフォーム依存 API のプラットフォーム名と一致している必要があります。 呼び出し元 API のプラットフォーム文字列にバージョンが含まれている場合は、次のようになります。

  • [SupportedOSPlatform("platformVersion")] 属性の場合、保護メソッドのプラットフォームの version は、呼び出し元のプラットフォームの Version 以上である必要があります。

  • [UnsupportedOSPlatform("platformVersion")] 属性の場合、保護メソッドのプラットフォームの version は、呼び出し元のプラットフォームの 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
        }
    }
    
  • 新しい OperatingSystem API を使用できない netstandard または netcoreapp をターゲットとするコードを保護する必要がある場合、RuntimeInformation.IsOSPlatform API が使用可能であり、アナライザーによって考慮されます。 ただし、これは OperatingSystem に追加された新しい API ほど最適化されていません。 プラットフォームが OSPlatform 構造体でサポートされていない場合は、OSPlatform.Create(String) を呼び出して、アナライザーでも考慮される、プラットフォーム名を渡すことができます。

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

プラットフォーム保護属性を使用して API に注釈を設定し、それをカスタム保護として使用する

前に示したように、アナライザーは、OperatingSystem.IsWindowsなどの OperatingSystem 型と RuntimeInformation.IsOSPlatform で、プラットフォーム保護の静的メソッドを認識します。 ただし、保護の結果をフィールドにキャッシュして再利用したり、カスタム保護メソッドを使用してプラットフォームをチェックしたりすることもできます。 アナライザーでは、そのような API をカスタム保護として認識する必要があり、それらによって保護されている API に対して警告を生成することはできません。 このシナリオをサポートするため、保護属性が .NET 6 で導入されました。

  • SupportedOSPlatformGuardAttribute では、SupportedOSPlatformAttribute によって注釈が付けられた API の保護として使用できる API に注釈が付けられます。
  • UnsupportedOSPlatformGuardAttribute では、UnsupportedOSPlatformAttribute によって注釈が付けられた API の保護として使用できる API に注釈が付けられます。

これらの属性には、必要に応じてバージョン番号を含めることができます。 それらを複数回適用して複数のプラットフォームを保護することができ、フィールド、プロパティ、またはメソッドに注釈を付けるために使用できます。

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

呼び出しサイトをプラットフォーム固有としてマークする

プラットフォーム名は、呼び出し元のプラットフォームに依存する API と一致している必要があります。 プラットフォーム文字列にバージョンが含まれている場合は、次のようになります。

  • [SupportedOSPlatform("platformVersion")] 属性の場合、呼び出しサイトのプラットフォームの version は、呼び出し元のプラットフォームの Version 以上である必要があります

  • [UnsupportedOSPlatform("platformVersion")] 属性の場合、呼び出しサイトのプラットフォームの version は、呼び出し元のプラットフォームの 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();
    }
    

プラットフォーム チェックを使用して呼び出しサイトをアサートする

プラットフォーム保護の例で使用されているすべての条件チェックは、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
}

関連項目