CA1416: Convalida compatibilità della piattaforma

Proprietà valore
ID regola CA1416
Title Convalida compatibilità della piattaforma
Categoria Interoperabilità
La correzione causa un'interruzione o meno Non causa un'interruzione
Abilitato per impostazione predefinita in .NET 8 Come avviso

Causa

Le violazioni vengono segnalate se un'API specifica della piattaforma viene usata nel contesto di una piattaforma diversa o se la piattaforma non è verificata (indipendente dalla piattaforma). Le violazioni vengono segnalate anche se viene usata un'API non supportata per la piattaforma di destinazione del progetto.

Questa regola è abilitata per impostazione predefinita solo per i progetti destinati a .NET 5 o versione successiva. È tuttavia possibile abilitarlo per i progetti destinati ad altri framework.

Descrizione regola

.NET 5 ha aggiunto nuovi attributi SupportedOSPlatformAttribute e UnsupportedOSPlatformAttribute, per annotare le API specifiche della piattaforma. È possibile creare un'istanza di entrambi gli attributi con o senza numeri di versione come parte del nome della piattaforma. Possono anche essere applicati più volte con piattaforme diverse.

  • Un'API non annotata viene considerata funzionante in tutte le piattaforme del sistema operativo.
  • Un'API contrassegnata con [SupportedOSPlatform("platformName")] è considerata portabile solo per le piattaforme del sistema operativo specificate. Se la piattaforma è un subset di un'altra piattaforma, l'attributo implica che tale piattaforma è supportata anche.
  • Un'API contrassegnata con [UnsupportedOSPlatform("platformName")] viene considerata non supportata nelle piattaforme del sistema operativo specificate. Se la piattaforma è un subset di un'altra piattaforma, l'attributo implica che anche tale piattaforma non è supportata.

È possibile combinare [SupportedOSPlatform] attributi e [UnsupportedOSPlatform] in una singola API. In questo caso, si applicano le regole seguenti:

  • Consenti elenco. Se la versione più bassa per ogni piattaforma del sistema operativo è un attributo [SupportedOSPlatform], l'API viene considerata supportata solo dalle piattaforme elencate e non supportata da tutte le altre piattaforme. L'elenco può avere un [UnsupportedOSPlatform] attributo con la stessa piattaforma, ma solo con una versione successiva, che indica che l'API viene rimossa da tale versione.
  • Elenco di rifiuto. Se la versione più bassa per ogni piattaforma del sistema operativo è un attributo [UnsupportedOSPlatform], l'API viene considerata non supportata solo dalle piattaforme elencate e supportata da tutte le altre piattaforme. L'elenco può avere un [SupportedOSPlatform] attributo con la stessa piattaforma, ma solo con una versione successiva, che indica che l'API è supportata da tale versione.
  • Elenco di elementi incoerenti. Se la versione più bassa per alcune piattaforme è [SupportedOSPlatform] ma [UnsupportedOSPlatform] per altre piattaforme, questa combinazione viene considerata incoerente. Alcune annotazioni nell'API vengono ignorate. In futuro, è possibile introdurre un analizzatore che genera un avviso in caso di incoerenza.

Se si accede a un'API annotata con questi attributi dal contesto di una piattaforma diversa, è possibile visualizzare violazioni CA1416.

Piattaforme di destinazione TFM

L'analizzatore non controlla le piattaforme di destinazione del moniker framework di destinazione (TFM) dalle proprietà di MSBuild, ad esempio <TargetFramework> o <TargetFrameworks>. Se tfm ha una piattaforma di destinazione, .NET SDK inserisce un SupportedOSPlatform attributo con il nome della piattaforma di destinazione nel file AssemblyInfo.cs , utilizzato dall'analizzatore. Ad esempio, se il TFM è net5.0-windows10.0.19041, l'SDK inserisce l'attributo [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] nel file AssemblyInfo.cs e l'intero assembly viene considerato solo Windows. Pertanto, la chiamata di API solo Windows con versione 7.0 o precedente non provocherebbe avvisi nel progetto.

Nota

Se la generazione di file AssemblyInfo.cs è disabilitata per il progetto ( ovvero la <GenerateAssemblyInfo> proprietà è impostata su false), l'attributo a livello SupportedOSPlatform di assembly richiesto non può essere aggiunto dall'SDK. In questo caso è possibile che vengano visualizzati avvisi per l'utilizzo di API specifiche della piattaforma anche se la piattaforma specifica è stata impostata come destinazione. Per risolvere gli avvisi, abilitare la generazione del file AssemblyInfo.cs o aggiungere manualmente l'attributo nel progetto.

Violazioni

  • Se si accede a un'API supportata solo in una piattaforma specificata ([SupportedOSPlatform("platformName")]) dal codice raggiungibile in altre piattaforme, verrà visualizzata la violazione seguente: 'API' è supportata in '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.
    }
    

    Nota

    Una violazione si verifica solo se il progetto non ha come destinazione la piattaforma supportata (net5.0-differentPlatform). Questo vale anche per i progetti con più destinazioni. Non si verifica alcuna violazione se il progetto è destinato alla piattaforma specificata (net5.0-platformName) e la generazione di file AssemblyInfo.cs è abilitata per il progetto.

  • L'accesso a un'API con attributo da [UnsupportedOSPlatform("platformName")] un contesto destinato alla piattaforma non supportata potrebbe generare una violazione: 'API' non è supportata in '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
    }
    

Nota

Se stai creando un'app che non ha come destinazione la piattaforma non supportata, non riceverai alcuna violazione. Una violazione si verifica solo nei casi seguenti:

  • Il progetto è destinato alla piattaforma con attributi non supportati.

  • è platformName incluso nel gruppo di elementi MSBuild <SupportedPlatform> predefinito.

  • platformName è incluso manualmente nel gruppo di elementi di MSBuild <SupportedPlatform> .

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

Come correggere le violazioni

Il modo consigliato per gestire le violazioni consiste nell'assicurarsi di chiamare solo API specifiche della piattaforma durante l'esecuzione in una piattaforma appropriata. A tale scopo, è possibile escludere il codice in fase di compilazione usando #if e multitargeting oppure chiamando in modo condizionale il codice in fase di esecuzione. L'analizzatore riconosce le guardie della piattaforma nella OperatingSystem classe e System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Eliminare le violazioni circondando il sito di chiamata con i metodi di protezione della piattaforma standard o le API di protezione personalizzata annotate con SupportedOSPlatformGuardAttribute o 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
        }
    }
    
  • L'analizzatore rispetta System.Diagnostics.Debug.Assert anche come mezzo per impedire che il codice venga raggiunto su piattaforme non supportate. L'uso Debug.Assert di consente di tagliare il controllo dalle build di versione, se necessario.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • È possibile scegliere di contrassegnare le proprie API come specifiche della piattaforma, inoltrando efficacemente i requisiti ai chiamanti. È possibile applicare gli attributi della piattaforma a una delle API seguenti:

    • Tipi
    • Membri (metodi, campi, proprietà ed eventi)
    • Assembly
    [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
    }
    
  • Quando viene applicato un attributo a livello di assembly o di tipo, tutti i membri all'interno dell'assembly o del tipo vengono considerati specifici della piattaforma.

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

Quando eliminare gli avvisi

Non è consigliabile fare riferimento ad API specifiche della piattaforma senza un contesto di piattaforma appropriato o protezione. Tuttavia, è possibile eliminare questi dati di diagnostica usando #pragma o il flag del compilatore NoWarn oppure impostando la gravità della regola su none in un file con estensione editorconfig.

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

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

Configurare il codice da analizzare

L'analizzatore è abilitato per impostazione predefinita solo per i progetti destinati a .NET 5 o versione successiva e hanno un valore AnalysisLevel di 5 o versione successiva. È possibile abilitarla per i framework di destinazione inferiori a net5.0 aggiungendo la coppia chiave-valore seguente a un file con estensione editorconfig nel progetto:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Vedi anche