CA1416:驗證平台相容性

規則識別碼 CA1416
類別 互通性
修正是中斷或非中斷 非中斷

原因

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

此規則預設只會針對以 .NET 5 或更新版本為目標的專案啟用。 不過,您可以針對以其他架構為目標的專案 啟用 它。

規則描述

.NET 5 已新增屬性 SupportedOSPlatformAttributeUnsupportedOSPlatformAttribute ,以標注平臺特定 API。 這兩個屬性都可以在平臺名稱中具現化或不含版本號碼。 您也可以使用不同的平臺多次套用它們。

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

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

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

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

TFM 目標平臺

分析器不會從 MSBuild 屬性檢查目標架構 Moniker (TFM) 目標平臺,例如 <TargetFramework><TargetFrameworks> 。 如果 TFM 具有目標平臺,.NET SDK 會在 Analyzer 所取用的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 檔案產生,或在專案中手動新增 屬性。

違規

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

  • 使用 或 標注 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。 不過,您可以使用 或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

另請參閱