CA1416:プラットフォームの互換性を検証する

ルール ID CA1416
カテゴリ 相互運用性
修正が中断ありか中断なしか なし

原因

プラットフォーム固有の API が別のプラットフォームのコンテキストで使用されているか、そのプラットフォームが検証されていない (プラットフォーム ニュートラル) 場合、違反が報告されます。 プロジェクトのターゲット プラットフォームでサポートされていない API が使用されている場合も、違反が報告されます。

このルールは、.NET 5 以降をターゲットとするプロジェクトに対してのみ、既定で有効です。 ただし、他のフレームワークをターゲットとするプロジェクトに対しても有効にすることができます。

規則の説明

.NET 5.0 では、プラットフォーム固有の API に注釈を付けるため、SupportedOSPlatformAttributeUnsupportedOSPlatformAttribute の新しい属性が追加されました。 どちらの属性も、プラットフォーム名の一部としてバージョン番号を含めても、含めなくてもインスタンス化できます。 これらは、異なるプラットフォームに複数回適用できます。

  • 注釈のない API は、すべてのオペレーティング システム (OS) プラットフォームで動作すると見なされます。
  • [SupportedOSPlatform("platformName")] でマークされた API は、指定した OS プラットフォームにのみ移植可能であると見なされます。 プラットフォームが別のプラットフォームのサブセットである場合、この属性では、そのプラットフォームもサポートされることを示唆します。
  • [UnsupportedOSPlatform("platformName")] でマークされた API は、指定した OS でサポートされていないと見なされます。 プラットフォームが別のプラットフォームのサブセットである場合、この属性では、そのプラットフォームもサポートされないことを示唆します。

[SupportedOSPlatform][UnsupportedOSPlatform] 属性は、1 つの API で組み合わせることができます。 この場合、次の規則が適用されます。

  • 許可リスト。 各 OS プラットフォームの最小バージョンが [SupportedOSPlatform] 属性である場合、その API はリストに含まれているプラットフォームでのみサポートされ、他のすべてのプラットフォームではサポートされないと見なされます。 このリストには、同じプラットフォームの [UnsupportedOSPlatform] 属性を含めることができますが、より上位のバージョンのみ可能で、これはそのバージョンでその API が削除されていることを意味します。
  • 拒否リスト。 各 OS プラットフォームの最小バージョンが [UnsupportedOSPlatform] 属性である場合、その API はリストに含まれているプラットフォームでのみサポートされず、他のすべてのプラットフォームではサポートされると見なされます。 このリストには、同じプラットフォームの [SupportedOSPlatform] 属性を含めることができますが、より上位のバージョンのみ可能で、これはそのバージョン以降その API がサポートされていることを意味します。
  • 不整合なリスト。 一部のプラットフォームの最小バージョンが [SupportedOSPlatform] であり、他のプラットフォームは [UnsupportedOSPlatform] である場合、この組み合わせは不整合と見なされます。 その API の一部の注釈は無視されます。 今後、不整合がある場合に警告を生成するアナライザーが導入される可能性があります。

別のプラットフォームのコンテキストからこれらの属性で注釈付けされた API にアクセスすると、CA1416 違反が表示されます。

TFM ターゲット プラットフォーム

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

注意

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

違反

  • 指定したプラットフォーム ([SupportedOSPlatform("platformName")]) でのみサポートされている API に、他のプラットフォーム上で到達可能なコードからアクセスした場合、次の違反が表示されます。 'API' は 'platformName' でサポートされています

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    

    注意

    そのプロジェクトが、サポートされているプラットフォーム (net5.0-differentPlatform) をターゲットとしていない場合にのみ、違反が発生します。 これは、複数のターゲットを持つプロジェクトにも当てはまります。 指定したプラットフォーム (net5.0-platformName) がプロジェクトでターゲットとされ、AssemblyInfo.csファイルの生成がプロジェクトに対して有効になっている場合、違反は発生しません。

  • サポートされていないプラットフォームをターゲットとするコンテキストから [UnsupportedOSPlatform("platformName")] 属性が設定された API にアクセスすると、次の違反が発生する場合があります。 'API' は 'platformName' ではサポートされていません

    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion() { }
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before
    }
    

    注意

    サポートされていないプラットフォームをターゲットとしないアプリを構築している場合、違反は発生しません。 違反は、次の場合にのみ発生します。

    • そのプロジェクトが、[unsupported](サポート対象外) という属性が設定されているプラットフォームをターゲットとしている。
    • platformName は、既定の MSBuild <SupportedPlatform> 項目グループに含まれています。
    • platformName が、手動で MSBuild <SupportedPlatform> 項目グループに含まれている。
    <ItemGroup>
        <SupportedPlatform Include="platformName" />
    </ItemGroup>
    

違反の修正方法

違反に対処するための推奨される方法は、適切なプラットフォーム上で実行される場合にのみプラットフォーム固有の API を呼び出すようにすることです。 これを実現するには、#if とマルチターゲットを使用してビルド時にコードを除外するか、実行時に条件付きでコードを呼び出すようにします。 アナライザーは、OperatingSystem クラスと System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform でプラットフォーム ガードを認識します。

  • 違反を抑制するには、標準のプラットフォーム ガードの方法または SupportedOSPlatformGuardAttributeUnsupportedOSPlatformGuardAttribute の注釈を使用したカスタム ガード API を使用し、呼び出しサイトを囲みます。

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    // API is supported on Windows, iOS from version 14.0, and MacCatalyst from version 14.0.
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")] // MacCatalyst is a superset of iOS, therefore it's also supported.
    public void SupportedOnWindowsIos14AndMacCatalyst14() { }
    
    public void Caller()
    {
        LinuxOnlyApi(); // This call site is reachable on all platforms. 'LinuxOnlyApi()' is only supported on: 'linux'.
    
        SupportedOnWindowsIos14AndMacCatalyst14(); // This call site is reachable on all platforms. 'SupportedOnWindowsIos14AndMacCatalyst14()'
                                                   // is only supported on: 'windows', 'ios' 14.0 and later, 'MacCatalyst' 14.0 and later.
    }
    
    [SupportedOSPlatformGuard("windows")]  // The platform guard attributes used
    [SupportedOSPlatformGuard("ios14.0")]
    private readonly bool _isWindowOrIOS14 = OperatingSystem.IsWindows() || OperatingSystem.IsIOSVersionAtLeast(14);
    
    // The warnings are avoided using platform guard methods.
    public void Caller()
    {
        if (OperatingSystem.IsLinux()) // standard guard examples
        {
            LinuxOnlyApi(); // no diagnostic
        }
    
        if (OperatingSystem.IsIOSVersionAtLeast(14))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    
        if (_isWindowOrMacOS14) // custom guard example
        {
            SupportedOnWindowsAndIos14(); // no diagnostic
        }
    }
    
    // An API not supported on Android but supported on all other platforms.
    [UnsupportedOSPlatform("android")]
    public void DoesNotWorkOnAndroid() { }
    
    // An API was unsupported on Windows until version 10.0.18362.
    // The API is considered supported everywhere else without constraints.
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    public void Caller()
    {
        DoesNotWorkOnAndroid(); // This call site is reachable on all platforms.'DoesNotWorkOnAndroid()' is unsupported on: 'android'
    
        StartedWindowsSupportFromCertainVersion(); // This call site is reachable on all platforms. 'StartedWindowsSupportFromCertainVersion()' is unsupported on: 'windows' 10.0.18362 and before.
    }
    
    [UnsupportedOSPlatformGuard("android")] // The platform guard attribute
    bool IsNotAndroid => !OperatingSystem.IsAndroid();
    
    public void Caller()
    {
        if (!OperatingSystem.IsAndroid()) // using standard guard methods
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    
        // Use the && and || logical operators to guard combined attributes.
        if (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18362))
        {
            StartedWindowsSupportFromCertainVersion(); // no diagnostic
        }
    
        if (IsNotAndroid) // custom guard example
        {
            DoesNotWorkOnAndroid(); // no diagnostic
        }
    }
    
  • アナライザーでは、サポートされていないプラットフォーム上でそのコードに到達できないようにするための手段として、System.Diagnostics.Debug.Assert も遵守されます。 必要に応じて Debug.Assert を使用することにより、このチェックをリリース ビルドからトリミングすることができます。

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • 独自の API をプラットフォーム固有としてマークすることで、要件を呼び出し元に効率的に送ることができます。 プラットフォーム属性は、次のどの API にも適用できます。

    • 種類
    • メンバー (メソッド、フィールド、プロパティ、イベント)
    • アセンブリ
    [SupportedOSPlatform("windows")]
    [SupportedOSPlatform("ios14.0")]
    public void SupportedOnWindowsAndIos14() { }
    
    [SupportedOSPlatform("ios15.0")] // call site version should be equal to or higher than the API version
    public void Caller()
    {
        SupportedOnWindowsAndIos14(); // No diagnostics
    }
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void StartedWindowsSupportFromCertainVersion();
    
    [UnsupportedOSPlatform("windows")]
    [SupportedOSPlatform("windows10.0.18362")]
    public void Caller()
    {
        StartedWindowsSupportFromCertainVersion(); // No diagnostics
    }
    
  • アセンブリまたは型レベルの属性が適用されると、そのアセンブリまたは型内のすべてのメンバーは、プラットフォーム固有であると見なされます。

    [assembly:SupportedOSPlatform("windows")]
    public namespace ns
    {
        public class Sample
        {
            public void SupportedOnWindows() { }
    
            public void Caller()
            {
                SupportedOnWindows(); // No diagnostic as call site and calling method both windows only
            }
        }
    }
    

どのようなときに警告を抑制するか

適切なプラットフォーム コンテキストやガードなしにプラットフォーム固有の API を参照することはお勧めできません。 ただし、#pragma または NoWarn コンパイラ フラグを使用するか、.editorconfig ファイルでルールの重大度を none に設定することで、これらの診断を抑制することもできます。

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

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

分析するコードを構成する

アナライザーは、.NET 5 以降をターゲットとし、かつ AnalysisLevel が 5 以上のプロジェクトに対してのみ、既定で有効になっています。 net5.0 未満のターゲット フレームワークの場合は、次のキーと値のペアをプロジェクト内の .editorconfig ファイルに追加することで、アナライザーを有効にできます。

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target=true

関連項目