Ú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 nemá mapování 1:1 na nativní reprezentaci. Například abstraktní popis obecné List<T>.Add
metody se mapuje na potenciálně nekonečné nativní tělo metody, které musí být specializované pro danou T
metodu (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 výjimka za běhu.
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 se po vytvoření nativní AOT neprotestují žádné změny chování vaší aplikace.
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. Uvažujte 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 nevyčerpat 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í sestavení AOT vydá na řádku následující upozornění 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.
V době běhu aplikace skutečně vyvolá výjimku z MakeGenericType
volání.
Reakce na upozornění AOT
Upozornění AOT mají přinést předvídatelnost nativních sestavení AOT. 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á v případě, že kód není v zásadě kompatibilní s AOT nebo je nativní závislost příliš složitá pro staticky predikci v době sestavení. To by často platilo pro metody, které používají Type.MakeGenericType
rozhraní API, generování reflexe nebo jiné technologie generování kódu za běhu. Následující kód znázorňuje 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ší oprava je vyhnout se volání metody vůbec při vytváření jako nativní AOT a použít něco jiného, co je kompatibilní s AOT. Pokud píšete knihovnu a není ve vašem ovládacím prvku, jestli chcete metodu volat, můžete ji také přidat RequiresDynamicCode
do vlastní metody. Tím se vaše metoda označí jako nekompatibilní s AOT. Když přidáte RequiresDynamicCode
všechna upozornění AOT v metodě s poznámkami, ale vygeneruje upozornění pokaždé, když ji někdo jiný zavolá. Z tohoto důvodu je většinou užitečné, aby autoři knihovny "bubliny" 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. Pří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 to podobné SuppressMessage
, ale můžete ho vidět pomocí publish
dalších nástrojů po sestavení. SuppressMessage
a #pragma
direktivy jsou přítomny pouze ve zdroji, takže je nelze použít k mlčení upozornění z sestavení.
Upozornění
Při potlačení upozornění AOT buďte opatrní. Volání může být teď kompatibilní s AOT, ale při aktualizaci kódu, který se může změnit, a možná zapomenete zkontrolovat všechna potlačení.