Partilhar via


CA1416: Validar a compatibilidade da plataforma

Property valor
ID da regra CA1416
Título Validar a compatibilidade da plataforma
Categoria Interoperabilidade
A correção está quebrando ou não quebrando Sem quebra
Habilitado por padrão no .NET 8 Como aviso

Motivo

As violações são relatadas se uma API específica da plataforma for usada no contexto de uma plataforma diferente ou se a plataforma não for verificada (neutra para a plataforma). As violações também são relatadas se uma API que não é suportada para a plataforma de destino do projeto for usada.

Esta regra é habilitada por padrão somente para projetos destinados ao .NET 5 ou posterior. No entanto, você pode habilitá-lo para projetos destinados a outras estruturas.

Descrição da regra

O .NET 5 adicionou novos atributos SupportedOSPlatformAttribute e UnsupportedOSPlatformAttribute, para anotar APIs específicas da plataforma. Ambos os atributos podem ser instanciados com ou sem números de versão como parte do nome da plataforma. Eles também podem ser aplicados várias vezes com diferentes plataformas.

  • Considera-se que uma API não anotada funciona em todas as plataformas de sistema operacional (SO).
  • Uma API marcada com [SupportedOSPlatform("platformName")] é considerada portátil apenas para as plataformas de SO especificadas. Se a plataforma for um subconjunto de outra plataforma, o atributo implica que essa plataforma também é suportada.
  • Uma API marcada com [UnsupportedOSPlatform("platformName")] é considerada não suportada nas plataformas de SO especificadas. Se a plataforma for um subconjunto de outra plataforma, o atributo implica que essa plataforma também não é suportada.

Você pode combinar [SupportedOSPlatform] atributos e [UnsupportedOSPlatform] em uma única API. Neste caso, aplicam-se as seguintes regras:

  • Lista de permissões. Se a versão mais baixa para cada plataforma de sistema operacional for um [SupportedOSPlatform] atributo, a API será considerada suportada apenas pelas plataformas listadas e não suportada por todas as outras plataformas. A lista pode ter um [UnsupportedOSPlatform] atributo com a mesma plataforma, mas apenas com uma versão superior, o que denota que a API é removida dessa versão.
  • Lista de negações. Se a versão mais baixa para cada plataforma de sistema operacional for um [UnsupportedOSPlatform] atributo, a API será considerada como não suportada apenas pelas plataformas listadas e suportada por todas as outras plataformas. A lista pode ter um [SupportedOSPlatform] atributo com a mesma plataforma, mas apenas com uma versão superior, o que denota que a API é suportada desde essa versão.
  • Lista inconsistente. Se a versão mais baixa para algumas plataformas for [SupportedOSPlatform] apenas [UnsupportedOSPlatform] para outras plataformas, essa combinação é considerada inconsistente. Algumas anotações na API são ignoradas. No futuro, podemos introduzir um analisador que produz um aviso em caso de inconsistência.

Se você acessar uma API anotada com esses atributos do contexto de uma plataforma diferente, poderá ver violações do CA1416.

Plataformas de destino TFM

O analisador não verifica as plataformas de destino do identificador da estrutura de destino (TFM) das propriedades do MSBuild, como <TargetFramework> ou <TargetFrameworks>. Se o TFM tiver uma plataforma de destino, o SDK do .NET injetará um SupportedOSPlatform atributo com o nome da plataforma de destino no arquivo AssemblyInfo.cs , que é consumido pelo analisador. Por exemplo, se o TFM for net5.0-windows10.0.19041, o SDK injetará o [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows10.0.19041")] atributo no arquivo AssemblyInfo.cs e todo o assembly será considerado somente Windows. Portanto, chamar APIs somente do Windows com versão 7.0 ou inferior não causaria nenhum aviso no projeto.

Nota

Se a geração de arquivo AssemblyInfo.cs estiver desabilitada para o projeto (ou seja, a <GenerateAssemblyInfo> propriedade estiver definida como false), o atributo de nível SupportedOSPlatform de assembly necessário não poderá ser adicionado pelo SDK. Nesse caso, você pode ver avisos para o uso de APIs específicas da plataforma, mesmo que esteja direcionando essa plataforma. Para resolver os avisos, habilite a geração de arquivo AssemblyInfo.cs ou adicione o atributo manualmente em seu projeto.

Violações

  • Se você acessar uma API suportada apenas em uma plataforma especificada ([SupportedOSPlatform("platformName")]) a partir de código acessível em outras plataformas, verá a seguinte violação: 'API' é suportada em '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

    Uma violação só ocorre se o projeto não tiver como alvo a plataforma suportada (net5.0-differentPlatform). O mesmo se aplica a projetos com várias orientações. Nenhuma violação ocorrerá se o projeto tiver como alvo a plataforma especificada (net5.0-platformName) e a geração de arquivos AssemblyInfo.cs estiver habilitada para o projeto.

  • Acessar uma API atribuída a [UnsupportedOSPlatform("platformName")] partir de um contexto direcionado à plataforma sem suporte pode produzir uma violação: 'API' não é suportada em '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 estiver a criar uma aplicação que não tenha como alvo a plataforma não suportada, não receberá quaisquer violações. Uma violação só ocorre nos seguintes casos:

  • O projeto tem como alvo a plataforma que é atribuída como não suportada.

  • O platformName está incluído no grupo de itens padrão do MSBuild <SupportedPlatform> .

  • platformName está incluído manualmente no grupo de <SupportedPlatform> itens do MSBuild.

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

Como corrigir violações

A maneira recomendada de lidar com violações é garantir que você chame apenas APIs específicas da plataforma quando executado em uma plataforma apropriada. Você pode conseguir isso excluindo o código em tempo de compilação usando #if e multi-targeting, ou chamando condicionalmente o código em tempo de execução. O analisador reconhece os protetores de OperatingSystem plataforma na classe e System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform.

  • Suprima violações cercando o site de chamada com os métodos de guarda de plataforma padrão ou APIs de guarda personalizada anotadas com SupportedOSPlatformGuardAttribute ou 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
        }
    }
    
  • O analisador também respeita System.Diagnostics.Debug.Assert como um meio para evitar que o código seja alcançado em plataformas não suportadas. O uso Debug.Assert permite que a verificação seja cortada das compilações de versão, se desejado.

    // An API supported only on Linux.
    [SupportedOSPlatform("linux")]
    public void LinuxOnlyApi() { }
    
    public void Caller()
    {
        Debug.Assert(OperatingSystem.IsLinux());
    
        LinuxOnlyApi(); // No diagnostic
    }
    
  • Você pode optar por marcar suas próprias APIs como sendo específicas da plataforma, encaminhando efetivamente os requisitos para seus chamadores. Você pode aplicar atributos de plataforma a qualquer uma das seguintes APIs:

    • Tipos
    • Membros (métodos, campos, propriedades e eventos)
    • Assemblagens
    [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 um atributo de nível de montagem ou nível de tipo é aplicado, todos os membros dentro do assembly ou tipo são considerados específicos da plataforma.

    [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 suprimir avisos

Não é recomendável fazer referência a APIs específicas da plataforma sem um contexto ou proteção de plataforma adequados. No entanto, você pode suprimir esses diagnósticos usando #pragma ou o sinalizador do compilador NoWarn ou definindo a severidade da regra como none em um arquivo .editorconfig.

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

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

Configurar código para análise

O analisador é habilitado por padrão apenas para projetos destinados ao .NET 5 ou posterior e com um AnalysisLevel de 5 ou superior. Você pode habilitá-lo para estruturas de destino inferiores net5.0 ao adicionando o seguinte par chave-valor a um arquivo .editorconfig em seu projeto:

dotnet_code_quality.enable_platform_analyzer_on_pre_net5_target = true

Consulte também