CA1416:驗證平台相容性

屬性
規則識別碼 CA1416
職稱 驗證平台相容性
類別 互通性
修正程式是中斷或非中斷 不中斷
預設在 .NET 8 中啟用 作為警告

原因

如果在不同平台的內容中使用平臺特定 API,或平臺未驗證(平臺中性),則會報告違規。 如果使用專案的目標平臺不支援的 API,也會報告違規。

預設只會針對以 .NET 5 或更新版本為目標的專案啟用此規則。 不過對於以其他架構為目標的專案,也能啟用分析器

檔案描述

.NET 5 已新增屬性和 UnsupportedOSPlatformAttributeSupportedOSPlatformAttribute以標註平臺特定 API。 無論版本號碼是否為平台名稱的一部份,這兩個屬性都可以具現化。 您也可以使用不同的平臺多次套用它們。

  • 未批注的 API 會被視為在所有作業系統 (OS) 平台上運作。
  • 標示為的 [SupportedOSPlatform("platformName")] API 只會被視為可移植到指定的 OS 平臺。 如果平臺是另一個 平臺的子集,則 屬性表示也支援該平臺。
  • [UnsupportedOSPlatform("platformName")] 指定的OS平臺上,標記為的API會被視為不受支援。 如果平臺是另一個 平臺的子集,則 屬性表示該平臺也不受支援。

您可以在單一 API 上結合 [SupportedOSPlatform][UnsupportedOSPlatform] 屬性。 在此情況下,適用下列規則:

  • 允許清單。 如果每個 OS 平台的最低版本是 [SupportedOSPlatform] 屬性,則 API 會視為只受列出的平台支援,而在所有其他平台上不受支援。 清單可以有 [UnsupportedOSPlatform] 具有相同平台的屬性,但只能使用較高版本,這表示 API 已從該版本中移除。
  • 拒絕清單。 如果每個 OS 平台的最低版本是 [UnsupportedOSPlatform] 屬性,則 API 會視為只在列出的平台上不受支援,而受所有其他平台支援。 此清單可以有 [SupportedOSPlatform] 具有相同平台的屬性,但只能使用較高版本,這表示自該版本以來支援 API。
  • 不一致清單。 如果某些平臺的最低版本是 [SupportedOSPlatform] ,但 [UnsupportedOSPlatform] 針對其他平臺,則此組合會被視為不一致。 API 上的一些批注會被忽略。 未來,我們可能會引進分析器,以在不一致的情況下產生警告。

如果您從不同平臺的內容存取以這些屬性標註的 API,您可以看到 CA1416 違規。

TFM 目標平臺

分析器不會從 MSBuild 屬性檢查目標 Framework Moniker (TFM) 目標平台,例如 <TargetFramework><TargetFrameworks>。 如果 TFM 具有目標平臺,.NET SDK 會在分析器取用的 AssemblyInfo.cs 檔案中,插入SupportedOSPlatform具有目標平臺名稱的屬性。 例如,如果 TFM 為 net5.0-windows10.0.19041,SDK 會將 [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] 屬性 插入AssemblyInfo.cs 檔案中,而且整個元件只會被視為 Windows。 因此,呼叫設定為 7.0 或以下版本的僅限 Windows 的 API,並不會在專案中造成任何警告。

注意

如果專案已停用AssemblyInfo.cs檔案產生功能(也就是 <GenerateAssemblyInfo> 屬性設定false為 ),SDK 就無法新增必要的元件層級SupportedOSPlatform屬性。 在此情況下,即使是以該平台為目標,仍會看到平台專屬 API 使用方式的警告。 若要解決警告,請啟用 AssemblyInfo.cs 檔案產生作業,或在專案中手動新增屬性。

違規

  • 如果您只從其他平臺上[SupportedOSPlatform("platformName")]可連線的程式代碼存取所支援的 API,您會看到下列違規: 『platformName』 支援 '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.
    }
    

    注意

    只有當專案不是以支援的平臺為目標時,才會發生違規。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
    }
    

注意

如果您要建置未以不支援平臺為目標的應用程式,則不會收到任何違規。 只有在下列情況下才會發生違規:

  • 專案是以屬性為不支援的平台為目標。

  • platformName包含在預設 MSBuild <SupportedPlatform> 專案群組中。

  • platformName 會手動包含在 MSBuild <SupportedPlatform> 專案群組中。

    <ItemGroup>
        <SupportedPlatform Include="platformName" />
    </ItemGroup>
    

如何修正違規

處理違規的建議方式是確保您只在適當的平台上執行時呼叫平臺特定 API。 您可以在建置階段使用 #if 和 多重目標排除程序代碼,或在運行時間有條件地呼叫程式代碼來達成此目的。 分析器可辨識 類別和 System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform中的OperatingSystem平臺防護。

  • 使用標準平臺防護方法或以 或UnsupportedOSPlatformGuardAttribute標註SupportedOSPlatformGuardAttribute的自定義防護 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。 不過,您可以使用 或 NoWarn 編譯程式旗標來隱藏這些診斷#pragma,或在 .editorconfig 檔案中將規則的嚴重性設定為 none

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

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

設定程式代碼以分析

分析器預設只會針對以 .NET 5 或更新版本為目標且 AnalysisLevel 為 5 或更高版本的項目啟用。 您可以將下列機碼/值組新增至專案中的 .editorconfig 檔案,以針對低於net5.0的目標架構啟用它:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

另請參閱