Sdílet prostřednictvím


Úvod k upozorněním AOT

Při publikování aplikace jako nativní AOT vytvoří proces sestavení veškerý nativní kód a datové struktury potřebné k podpoře aplikace za běhu. To se liší od ne nativních nasazení, která spouští aplikaci z formátů, které popisují aplikaci v abstraktních termínech (program pro virtuální počítač) a vytvářejí nativní reprezentace na vyžádání za běhu.

Abstraktní reprezentace částí programu nemají mapování 1:1 na nativní reprezentaci. Například abstraktní popis obecné List<T>.Add metody se mapuje na potenciálně nekonečné množství nativních těl metod, které musí být specializované pro danou T (například List<int>.Add a List<double>.Add).

Vzhledem k tomu, že vztah abstraktního kódu k nativnímu kódu není 1:1, musí proces sestavení vytvořit úplný seznam nativních těl kódu a datových struktur v době sestavení. Pro některá rozhraní API .NET může být obtížné vytvořit tento seznam v době sestavení. Pokud se rozhraní API používá způsobem, který se v době sestavení neočekával, vyvolá se za běhu výjimka.

Aby se zabránilo změnám chování při nasazování jako nativní AOT, poskytuje sada .NET SDK statickou analýzu kompatibility AOT prostřednictvím upozornění AOT. Upozornění AOT se vytvoří, když sestavení najde kód, který nemusí být kompatibilní s AOT. Kód, který není kompatibilní s AOT, může způsobit změny chování nebo dokonce chybové ukončení v aplikaci poté, co je vytvořen jako nativní AOT. V ideálním případě by všechny aplikace, které používají nativní AOT, neměly obsahovat žádná upozornění AOT. Pokud existují nějaká upozornění AOT, ujistěte se, že po sestavení jako Native AOT nedochází k žádným změnám chování vaší aplikace důkladným otestováním.

Příklady upozornění AOT

U většiny kódu jazyka C# je jednoduché určit, jaký nativní kód je potřeba vygenerovat. Nativní kompilátor může procházet těla metod a najít, k jakým nativním strukturám kódu a dat se přistupuje. Některé funkce, jako je reflexe, bohužel představují významný problém. Vezměte v úvahu následující kód:

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

struct GenericType<T> { }

I když výše uvedený program není velmi užitečný, představuje extrémní případ, který vyžaduje nekonečné množství obecných typů, které se mají vytvořit při vytváření aplikace jako nativní AOT. Bez nativní AOT by program běžel, dokud by nevyčerpal paměť. S nativní AOT bychom ho nemohli ani sestavit, pokud bychom vygenerovali všechny potřebné typy (nekonečný počet).

V tomto případě nativní AOT kompilace vydá následující upozornění na řádku 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.

Za běhu aplikace skutečně vyvolá výjimku z MakeGenericType volání.

Reakce na upozornění AOT

Výstrahy AOT jsou určeny k tomu, aby zajistily předvídatelnost pro nativní AOT sestavení. Většina upozornění AOT se týká možných výjimek za běhu v situacích, kdy se nativní kód negeneroval pro podporu scénáře. Nejobsáhlejší kategorií je RequiresDynamicCodeAttribute.

VyžadujeDynamicCode

RequiresDynamicCodeAttribute je jednoduchý a široký: jedná se o atribut, který znamená, že člen byl označen jako nekompatibilní s AOT. Tato poznámka znamená, že člen může použít reflexi nebo jiný mechanismus k vytvoření nového nativního kódu za běhu. Tento atribut se používá, když je kód v zásadě nekompatibilní s AOT, nebo když je nativní závislost příliš složitá na to, aby ji bylo možné staticky predikovat v době sestavení. To by často platilo pro metody, které používají Type.MakeGenericType API rozhraní, emitování reflexe nebo jiné technologie modulu runtime pro generování kódu. Následující kód ukazuje příklad.

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

Neexistuje mnoho alternativních řešení pro RequiresDynamicCode. Nejlepší postup je úplně se vyhnout volání této metody při sestavování jako nativní AOT a použít něco jiného, co je kompatibilní s AOT. Pokud píšete knihovnu a není ve vaší moci, zda voláte metodu, můžete také přidat RequiresDynamicCode k vaší vlastní metodě. Tím se vaše metoda označí jako nekompatibilní s AOT. Přidání RequiresDynamicCode ztiší všechna upozornění AOT v anotované metodě, ale vyvolá upozornění pokaždé, když ji někdo jiný zavolá. Z tohoto důvodu je většinou užitečné, aby autoři knihoven vyzdvihli upozornění na veřejné rozhraní API.

Pokud můžete nějakým způsobem zjistit, že volání je bezpečné a veškerý nativní kód bude k dispozici za běhu, můžete také potlačit upozornění pomocí UnconditionalSuppressMessageAttribute. Například:

[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 je podobný SuppressMessage, ale jde jej vidět nejen pomocí publish, ale i jiných nástrojů po sestavení. SuppressMessage a #pragma direktivy jsou přítomny pouze ve zdroji, takže je nelze použít k potlačení varování během sestavení.

Upozornění

Při zrušení upozornění AOT buďte opatrní. Volání může být teď kompatibilní s AOT, ale při aktualizaci kódu se to může změnit, a možná zapomenete projít všechna potlačení.