Partilhar via


Introdução aos avisos AOT

Ao publicar a sua aplicação como AOT nativo, o processo de compilação produz todo o código nativo e as estruturas de dados necessárias para suportar a aplicação em tempo de execução. Isto é diferente das implementações não nativas, que executam a aplicação a partir de formatos que descrevem a aplicação em termos abstratos (um programa para uma máquina virtual) e criam representações nativas sob demanda em tempo de execução.

As representações abstratas de componentes do programa não têm uma correspondência direta com a representação nativa. Por exemplo, a descrição abstrata do método genérico List<T>.Add mapeia-se em corpos nativos de método potencialmente infinitos que precisam ser especializados em relação ao T dado (por exemplo, List<int>.Add e List<double>.Add).

Como a relação do código abstrato com o código nativo não é um-para-um, o processo de compilação precisa criar uma lista completa de corpos de código nativos e estruturas de dados no momento da compilação. Pode ser difícil criar essa lista em tempo de compilação para algumas das APIs .NET. Se a API for usada de uma forma que não foi antecipada em tempo de compilação, será lançada uma exceção em tempo de execução.

Para evitar alterações no comportamento ao implantar como AOT nativo, o SDK do .NET fornece análise estática da compatibilidade de AOT por meio de "avisos de AOT". Os avisos de AOT são produzidos quando a compilação encontra código que pode não ser compatível com AOT. O código que não é compatível com AOT pode produzir alterações comportamentais ou até mesmo falhas em um aplicativo depois que ele foi criado como AOT nativo. Idealmente, todas as aplicações que utilizam AOT nativo não devem apresentar avisos de AOT. Se houver algum aviso de AOT, certifique-se de que não haja alterações de comportamento testando completamente a sua aplicação depois de construir como AOT Nativo.

Exemplos de avisos AOT

Para a maioria dos códigos C#, é simples determinar qual código nativo precisa ser gerado. O compilador nativo pode percorrer os corpos do método e encontrar quais códigos nativos e estruturas de dados são acessados. Infelizmente, algumas características, como a reflexão, apresentam um problema significativo. Considere o seguinte código:

Type t = typeof(int);
while (true)
{
    t = typeof(GenericType<>).MakeGenericType(t);
    Console.WriteLine(Activator.CreateInstance(t));
}

struct GenericType<T> { }

Embora o programa acima não seja muito útil, ele representa um caso extremo que requer um número infinito de tipos genéricos a serem criados ao construir o aplicativo como AOT nativo. Sem AOT nativo, o programa seria executado até ficar sem memória. Com o AOT nativo, não seríamos capazes de construí-lo se gerássemos todos os tipos necessários (o número infinito deles).

Nesse caso, a compilação nativa AOT emite o seguinte aviso na linha MakeGenericType.

AOT analysis warning IL3050: Program.<Main>$(String[]): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

Em tempo de execução, a aplicação irá de fato lançar uma exceção da chamada MakeGenericType.

Reagir aos avisos da AOT

Os avisos de AOT têm como objetivo proporcionar previsibilidade às compilações nativas de AOT. A maioria dos avisos AOT refere-se a possíveis exceções em tempo de execução em situações em que não foi gerado código nativo para suportar o cenário. A categoria mais ampla é RequiresDynamicCodeAttribute.

RequerCódigoDinâmico

RequiresDynamicCodeAttribute é simples e amplo: é um atributo que significa que o membro foi anotado como sendo incompatível com AOT. Esta anotação significa que o membro pode usar reflexão ou outro mecanismo para criar novo código nativo em tempo de execução. Esse atributo é usado quando o código não é fundamentalmente compatível com AOT ou a dependência nativa é muito complexa para prever estaticamente em tempo de compilação. Isto seria frequentemente verdade para métodos que utilizam API Type.MakeGenericType , emissão por reflexão ou outras tecnologias de geração de código em tempo de execução. O código a seguir mostra um exemplo.

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

void TestMethod()
{
    // IL3050: Using method 'MethodWithReflectionEmit' which has 'RequiresDynamicCodeAttribute'
    // can break functionality when AOT compiling. Use 'MethodFriendlyToAot' instead.
    MethodWithReflectionEmit();
}

Não há muitas soluções alternativas para RequiresDynamicCode. A melhor correção é evitar chamar o método ao compilar como AOT nativo e usar outra alternativa que seja compatível com AOT. Se estiver a escrever uma biblioteca e não estiver sob o seu controlo decidir chamar ou não o método, poderá também adicionar RequiresDynamicCode ao seu próprio método. Isso anotará seu método como não compatível com AOT. Adicionar RequiresDynamicCode silencia todos os avisos AOT no método anotado, mas gerará um aviso sempre que outra pessoa chamá-lo. Por esse motivo, é mais útil para os autores de bibliotecas fazer com que o aviso "suba" para uma API pública.

Se conseguir de alguma forma determinar que a chamada está segura e que todo o código nativo estará disponível em tempo de execução, também pode suprimir o aviso usando UnconditionalSuppressMessageAttribute. Por exemplo:

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

[UnconditionalSuppressMessage("Aot", "IL3050:RequiresDynamicCode",
    Justification = "The unfriendly method is not reachable with AOT")]
void TestMethod()
{
    If (RuntimeFeature.IsDynamicCodeSupported)
        MethodWithReflectionEmit(); // warning suppressed
}

UnconditionalSuppressMessage é como SuppressMessage , mas pode ser visto por publish e outras ferramentas pós-compilação. SuppressMessage e #pragma as diretivas estão presentes apenas no código-fonte, por isso não podem ser usadas para silenciar avisos da build.

Atenção

Tenha cuidado ao suprimir avisos de AOT. A chamada pode ser compatível com AOT agora, mas à medida que atualizas o teu código, isso pode mudar e podes esquecer-te de rever todas as supressões.