Bagikan melalui


CA1416: Validasi kompatibilitas platform

Properti Nilai
ID Aturan CA1416
Judul Memvalidasi kompatibilitas platform
Golongan Interoperabilitas
Perbaikan bersifat disruptif atau non-disruptif Non-disruptif
Diaktifkan secara default di .NET 8 Sebagai peringatan

Penyebab

Pelanggaran dilaporkan jika API khusus platform digunakan dalam konteks platform yang berbeda atau jika platform tidak diverifikasi (netral platform). Pelanggaran juga dilaporkan jika API yang tidak didukung untuk platform target proyek digunakan.

Aturan ini diaktifkan secara default hanya untuk proyek yang menargetkan .NET 5 atau yang lebih baru. Namun, Anda dapat mengaktifkannya untuk proyek yang menargetkan kerangka kerja lain.

Deskripsi aturan

.NET 5 menambahkan atribut baru, SupportedOSPlatformAttribute dan UnsupportedOSPlatformAttribute, untuk membuat anotasi API khusus platform. Kedua atribut dapat dipakai dengan atau tanpa nomor versi sebagai bagian dari nama platform. Mereka juga dapat diterapkan beberapa kali dengan platform yang berbeda.

  • API tanpa catatan dianggap berfungsi di semua platform sistem operasi (OS).
  • API yang ditandai dengan [SupportedOSPlatform("platformName")] dianggap portabel untuk platform OS yang ditentukan saja. Jika platform adalah bagian dari platform lain, atribut tersebut menyiratkan bahwa platform tersebut juga didukung.
  • API yang ditandai dengan [UnsupportedOSPlatform("platformName")] dianggap tidak didukung pada platform OS yang ditentukan. Jika platform adalah bagian dari platform lain, atribut tersebut menyiratkan bahwa platform tersebut juga tidak didukung.

Anda dapat menggabungkan atribut [SupportedOSPlatform] dan [UnsupportedOSPlatform] pada satu API. Dalam hal ini, aturan berikut berlaku:

  • Daftar yang diizinkan. Jika versi terendah untuk setiap platform OS adalah atribut [SupportedOSPlatform], API dianggap hanya didukung oleh platform yang terdaftar dan tidak didukung oleh semua platform lainnya. Daftar tersebut dapat memiliki atribut [UnsupportedOSPlatform] dengan platform yang sama, tetapi hanya dengan versi yang lebih tinggi, yang menunjukkan bahwa API dihapus dari versi tersebut.
  • Daftar yang ditolak. Jika versi terendah untuk setiap platform OS adalah atribut [UnsupportedOSPlatform], maka API dianggap hanya tidak didukung oleh platform yang terdaftar dan didukung oleh semua platform lainnya. Daftar tersebut dapat memiliki atribut [SupportedOSPlatform] dengan platform yang sama, tetapi hanya dengan versi yang lebih tinggi, yang menunjukkan bahwa API didukung sejak versi tersebut.
  • Daftar yang tidak konsisten. Jika versi terendah untuk beberapa platform adalah [SupportedOSPlatform] tetapi [UnsupportedOSPlatform] untuk platform lain, kombinasi ini dianggap tidak konsisten. Beberapa anotasi pada API diabaikan. Di masa mendatang, kami dapat memperkenalkan penganalisis yang menghasilkan peringatan jika terjadi ketidakkonsistenan.

Jika Anda mengakses API yang dianotasi dengan atribut ini dari konteks platform yang berbeda, Anda dapat melihat pelanggaran CA1416.

Platform target TFM

Penganalisis tidak memeriksa target kerangka kerja moniker (TFM) platform target dari properti MSBuild, seperti <TargetFramework> atau <TargetFrameworks>. Jika TFM memiliki platform target, .NET SDK menyuntikkan atribut SupportedOSPlatform dengan nama platform yang ditargetkan dalam file AssemblyInfo.cs, yang digunakan oleh penganalisis. Misalnya, jika TFM adalah net5.0-windows10.0.19041, SDK memasukkan atribut [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] ke dalam file AssemblyInfo.cs, dan seluruh rakitan dianggap hanya untuk Windows. Oleh karena itu, memanggil API khusus Windows dengan versi 7.0 atau di bawahnya tidak akan menyebabkan peringatan apa pun dalam proyek.

Catatan

Jika pembuatan file AssemblyInfo.cs dinonaktifkan untuk proyek (yaitu, properti <GenerateAssemblyInfo> diatur ke false), atribut SupportedOSPlatform tingkat rakitan yang diperlukan tidak dapat ditambahkan oleh SDK. Dalam hal ini, Anda dapat melihat peringatan untuk penggunaan API khusus platform meskipun Anda menargetkan platform tersebut. Untuk mengatasi peringatan, aktifkan pembuatan file AssemblyInfo.cs atau tambahkan atribut secara manual di proyek Anda.

Pelanggaran

  • Jika Anda mengakses API yang hanya didukung pada platform tertentu ([SupportedOSPlatform("platformName")]) dari kode yang dapat dijangkau di platform lain, Anda akan melihat pelanggaran berikut: 'API' didukung di '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.
    }
    

    Catatan

    Pelanggaran hanya terjadi jika proyek tidak menargetkan platform yang didukung (net5.0-differentPlatform). Ini juga berlaku untuk proyek multi-target. Tidak ada pelanggaran yang terjadi jika proyek menargetkan platform yang ditentukan (net5.0-platformName) dan pembuatan file AssemblyInfo.cs diaktifkan untuk proyek tersebut.

  • Mengakses API yang dikaitkan dengan [UnsupportedOSPlatform("platformName")] dari konteks yang menargetkan platform yang tidak didukung dapat menghasilkan pelanggaran: 'API' tidak didukung di '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
    }
    

Catatan

Jika membuat aplikasi yang tidak menargetkan platform yang tidak didukung, Anda tidak akan mendapatkan pelanggaran apa pun. Pelanggaran hanya terjadi dalam kasus-kasus berikut:

  • Proyek ini menargetkan platform yang dikaitkan sebagai tidak didukung.

  • platformName disertakan dalam grup item MSBuild <SupportedPlatform> default.

  • platformName secara manual disertakan dalam grup item <SupportedPlatform> MSBuild.

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

Cara memperbaiki pelanggaran

Cara yang disarankan untuk menangani pelanggaran adalah memastikan Anda hanya memanggil API khusus platform saat berjalan di platform yang sesuai. Anda dapat mencapai ini dengan mengecualikan kode pada waktu pembuatan menggunakan #if dan multi-penargetan, atau dengan memanggil kode secara kondisional pada waktu berjalan. Penganalisis mengenali pelindung platform di kelas OperatingSystem dan System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Menyembunyikan pelanggaran dengan mengelilingi situs panggilan dengan metode penjaga platform standar atau API penjaga khusus yang dianotasi dengan SupportedOSPlatformGuardAttribute atau UnsupportedOSPlatformGuardAttribute.

    // 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
        }
    }
    
  • Penganalisis juga menerapkan System.Diagnostics.Debug.Assert sebagai sarana untuk mencegah kode agar tidak dijangkau pada platform yang tidak didukung. Menggunakan Debug.Assert memungkinkan pemeriksaan dipangkas dari build rilis, jika diinginkan.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • Anda dapat memilih untuk menandai API Anda sendiri sebagai khusus platform, yang secara efektif meneruskan persyaratan ke pemanggil Anda. Anda dapat menerapkan atribut platform ke salah satu API berikut:

    • Jenis
    • Anggota (metode, bidang, properti, dan peristiwa)
    • Rakitan
    [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
    }
    
  • Ketika atribut tingkat rakitan atau tingkat jenis diterapkan, semua anggota dalam rakitan atau jenis dianggap spesifik platform.

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

Kapan harus menekan peringatan

Mereferensikan API khusus platform tanpa konteks atau pelindung platform yang tepat tidak disarankan. Namun, Anda dapat menyembunyikan diagnostik ini menggunakan #pragma atau bendera pengompilasi NoWarn, atau dengan mengatur tingkat keparahan aturan ke none di file .editorconfig.

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

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

Mengonfigurasi kode yang akan dianalisis

Penganalisis diaktifkan secara default hanya untuk proyek yang menargetkan .NET 5 atau yang lebih baru dan memiliki AnalysisLevel 5 atau lebih tinggi. Anda dapat mengaktifkannya untuk kerangka kerja target yang lebih rendah dari net5.0 dengan menambahkan pasangan nilai kunci berikut ke file .editorconfig di proyek Anda:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Lihat juga