Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Quando si pubblica l'applicazione come AOT nativo, il processo di compilazione produce tutto il codice nativo e le strutture di dati necessarie per supportare l'applicazione in fase di esecuzione. Ciò è diverso dalle distribuzioni non native, che eseguono l'applicazione dai formati che descrivono l'applicazione in termini astratti (un programma per una macchina virtuale) e creano rappresentazioni native su richiesta in fase di esecuzione.
Le rappresentazioni astratte delle parti del programma non corrispondono direttamente alla rappresentazione nativa. Ad esempio, la descrizione astratta del metodo generico List<T>.Add
è mappata a corpi di metodi nativi potenzialmente infiniti che devono essere specializzati per il dato T
(ad esempio, List<int>.Add
e List<double>.Add
).
Poiché la relazione tra codice astratto e codice nativo non è uno-a-uno, il processo di compilazione deve creare un elenco completo di corpi di codice nativi e strutture di dati in fase di compilazione. Può essere difficile creare questo elenco in fase di compilazione per alcune api .NET. Se l'API viene usata in modo non previsto in fase di compilazione, verrà generata un'eccezione in fase di esecuzione.
Per evitare modifiche nel comportamento durante la distribuzione come AOT nativo, .NET SDK fornisce un'analisi statica della compatibilità AOT tramite avvisi AOT. Gli avvisi AOT vengono generati quando la compilazione trova codice che potrebbe non essere compatibile con AOT. Il codice non compatibile con AOT può produrre modifiche comportamentali o persino arresti anomali in un'applicazione dopo che è stato compilato come AOT nativo. Idealmente, tutte le applicazioni che usano Native AOT non devono avere avvisi AOT. Se sono presenti avvisi AOT, verificare che non ci siano cambiamenti nel comportamento dell'applicazione testando accuratamente l'app dopo la compilazione come AOT Nativo.
Esempi di avvisi AOT
Per la maggior parte del codice C#, è semplice determinare quale codice nativo deve essere generato. Il compilatore nativo può esaminare i corpi dei metodi e individuare il codice nativo e le strutture di dati a cui si accede. Sfortunatamente, alcune funzionalità, come la reflection, presentano un problema significativo. Osservare il codice seguente:
Type t = typeof(int);
while (true)
{
t = typeof(GenericType<>).MakeGenericType(t);
Console.WriteLine(Activator.CreateInstance(t));
}
struct GenericType<T> { }
Anche se il programma precedente non è molto utile, rappresenta un caso estremo che richiede la creazione di un numero infinito di tipi generici durante la compilazione dell'applicazione come AOT nativo. Senza AOT nativo, il programma verrebbe eseguito fino a quando non esaurisce la memoria. Con AOT nativo, non saremmo in grado di compilarlo anche se fosse necessario generare tutti i tipi necessari (il numero infinito di essi).
In questo caso, la compilazione AOT nativa genera l'avviso seguente nella MakeGenericType
riga:
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.
In fase di esecuzione, l'applicazione genererà effettivamente un'eccezione dalla chiamata MakeGenericType
.
Rispondere agli avvisi AOT
Gli avvisi AOT sono concepiti per portare la prevedibilità alle compilazioni AOT native. La maggior parte degli avvisi AOT riguarda possibili eccezioni in fase di esecuzione in situazioni in cui il codice nativo non è stato generato per supportare lo scenario. La categoria più ampia è RequiresDynamicCodeAttribute
.
RichiedeCodiceDinamico
RequiresDynamicCodeAttribute è semplice e ampio: si tratta di un attributo che indica che il membro è stato annotato come incompatibile con AOT. Questa annotazione indica che il membro potrebbe usare reflection o un altro meccanismo per creare un nuovo codice nativo in fase di esecuzione. Questo attributo viene usato quando il codice non è fondamentalmente compatibile con AOT o la dipendenza nativa è troppo complessa per stimare in modo statico in fase di compilazione. Questo vale spesso per i metodi che usano l'API, la Type.MakeGenericType
reflection emit o altre tecnologie di generazione di codice in fase di esecuzione. Il codice seguente illustra un esempio.
[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();
}
Non esistono molte soluzioni alternative per RequiresDynamicCode
. La correzione migliore consiste nell'evitare di chiamare il metodo quando si compila come AOT nativo e usare qualcos'altro compatibile con AOT. Se si sta scrivendo una libreria e non è sotto il tuo controllo chiamare o meno il metodo, puoi anche aggiungere RequiresDynamicCode
al tuo metodo. In questo modo il metodo verrà annotato come non compatibile con AOT. L'aggiunta di RequiresDynamicCode
silenzia tutti gli avvisi AOT nel metodo annotato, ma genererà un avviso ogni volta che viene chiamato da qualcun altro. Per questo motivo, è soprattutto utile per gli autori di librerie "creare un'anteprima" dell'avviso a un'API pubblica.
Se in qualche modo è possibile determinare che la chiamata è sicura e tutto il codice nativo sarà disponibile in fase di esecuzione, è anche possibile eliminare l'avviso usando UnconditionalSuppressMessageAttribute. Per esempio:
[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
è come SuppressMessage
ma può essere visto da publish
e altri strumenti di post-compilazione.
SuppressMessage
e le direttive #pragma
sono presenti solo nell'origine, quindi non possono essere usate per disattivare gli avvisi durante la compilazione.
Attenzione
Prestare attenzione quando si sopprimono gli avvisi AOT. La chiamata potrebbe essere compatibile con AOT al momento, ma quando aggiornerai il codice, questo potrebbe cambiare e potresti dimenticare di rivedere tutte le soppressioni.