Dela via


Introduktion till AOT-varningar

När du publicerar ditt program som intern AOT skapar byggprocessen alla interna kod- och datastrukturer som krävs för att stödja programmet vid körning. Detta skiljer sig från icke-interna distributioner, som kör programmet från format som beskriver programmet i abstrakta termer (ett program för en virtuell dator) och skapar interna representationer på begäran vid körning.

De abstrakta representationerna av programdelar har ingen en-till-en-mappning till den interna representationen. Till exempel mappar den abstrakta beskrivningen av den generiska List<T>.Add metoden till potentiellt oändliga inbyggda metodkroppar som måste vara specialiserade för den angivna T (till exempel List<int>.Add och List<double>.Add).

Eftersom relationen mellan abstrakt kod och intern kod inte är en-till-en måste byggprocessen skapa en fullständig lista över inbyggda kodkroppar och datastrukturer vid bygget. Det kan vara svårt att skapa den här listan vid byggtiden för några av .NET-API:erna. Om API:et används på ett sätt som inte förväntades vid bygget genereras ett undantag vid körning.

För att förhindra ändringar i beteende vid distribution som intern AOT tillhandahåller .NET SDK statisk analys av AOT-kompatibilitet via "AOT-varningar". AOT-varningar skapas när bygget hittar kod som kanske inte är kompatibel med AOT. Kod som inte är AOT-kompatibel kan ge beteendeförändringar eller till och med kraschar i ett program efter att den har skapats som intern AOT. Helst bör alla program som använder intern AOT inte ha några AOT-varningar. Om det finns AOT-varningar kontrollerar du att det inte finns några beteendeändringar genom att noggrant testa din app efter att du har skapat som intern AOT.

Exempel på AOT-varningar

För de flesta C#-kod är det enkelt att avgöra vilken intern kod som behöver genereras. Den inbyggda kompilatorn kan gå igenom metodkropparna och hitta vilka interna kod- och datastrukturer som används. Tyvärr utgör vissa funktioner, till exempel reflektion, ett betydande problem. Ta följande kod som exempel:

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

struct GenericType<T> { }

Även om ovanstående program inte är särskilt användbart representerar det ett extremt fall som kräver att ett oändligt antal generiska typer skapas när programmet skapas som intern AOT. Utan intern AOT skulle programmet köras tills minnet tar slut. Med intern AOT skulle vi inte ens kunna skapa den om vi skulle generera alla nödvändiga typer (det oändliga antalet av dem).

I det här fallet utfärdar native AOT-kompilering följande varning på MakeGenericType raden:

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.

Vid körningen utlöser programmet ett undantag från anropet MakeGenericType .

Reagera på AOT-varningar

AOT-varningarna är avsedda att ge förutsägbarhet till interna AOT-versioner. En majoritet av AOT-varningar handlar om möjliga körningsfel i situationer då inbyggd kod inte genererades för att stödja scenariot. Den bredaste kategorin är RequiresDynamicCodeAttribute.

KräverDynamicCode

RequiresDynamicCodeAttribute är enkelt och brett: det är ett attribut som innebär att medlemmen har kommenterats som inkompatibel med AOT. Den här kommentaren innebär att medlemmen kan använda reflektion eller någon annan mekanism för att skapa ny intern kod vid körning. Det här attributet används när koden i grunden inte är AOT-kompatibel, eller om det interna beroendet är för komplext för att statiskt förutsäga vid byggtid. Detta gäller ofta för metoder som använder API:et Type.MakeGenericType , reflektionsemittenten eller andra körningskodgenereringstekniker. Följande kod visar ett exempel.

[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();
}

Det finns inte många lösningar för RequiresDynamicCode. Den bästa korrigeringen är att undvika att anropa metoden alls när du skapar som intern AOT och använda något annat som är AOT-kompatibelt. Om du skriver ett bibliotek och det inte står i din kontroll om du vill anropa metoden eller inte, kan du också lägga till RequiresDynamicCode i din egen metod. Detta kommenterar din metod som inte AOT-kompatibel. Om du lägger till RequiresDynamicCode tystar du alla AOT-varningar i den kommenterade metoden, men skapar en varning när någon annan anropar den. Därför är det mest användbart för biblioteksförfattare att "bubbla upp" varningen till ett offentligt API.

Om du på något sätt kan fastställa att anropet är säkert och all intern kod kommer att vara tillgänglig vid körning kan du också ignorera varningen med .UnconditionalSuppressMessageAttribute Till exempel:

[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 är som SuppressMessage men det kan ses av publish och andra verktyg efter bygget. SuppressMessage och #pragma direktiv finns bara i källan, så de kan inte användas för att tysta varningar från bygget.

Varning

Var försiktig när du undertrycker AOT-varningar. Anropet kan vara AOT-kompatibelt nu, men när du uppdaterar koden kan det ändras och du kanske glömmer att granska alla undertryckningar.