Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Al publicar la aplicación como AOT nativo, el proceso de compilación genera todo el código nativo y las estructuras de datos necesarias para admitir la aplicación en tiempo de ejecución. Esto es diferente de las implementaciones no nativas, que ejecutan la aplicación a partir de formatos que describen la aplicación en términos abstractos (un programa para una máquina virtual) y crean representaciones nativas a petición en tiempo de ejecución.
Las representaciones abstractas de las partes del programa no tienen una correspondencia uno a uno con una representación nativa. Por ejemplo, la descripción abstracta del método List<T>.Add
genérico se asigna a cuerpos de método nativos potencialmente infinitos que deben estar especializados para el objeto T
especificado (por ejemplo, List<int>.Add
y List<double>.Add
).
Dado que la relación de código abstracto con código nativo no es uno a uno, el proceso de compilación debe crear una lista completa de cuerpos de código nativos y estructuras de datos en tiempo de compilación. Puede ser difícil crear esta lista en tiempo de compilación para algunas de las API de .NET. Si la API se usa de una manera que no se esperaba en tiempo de compilación, se producirá una excepción en tiempo de ejecución.
Para evitar cambios en el comportamiento al implementar como AOT nativo, el SDK de .NET proporciona un análisis estático de la compatibilidad de AOT a través de "advertencias de AOT". Las advertencias de AOT se producen cuando la compilación encuentra código que puede no ser compatible con AOT. El código que no es compatible con AOT puede producir cambios de comportamiento o incluso bloqueos en una aplicación después de que se haya compilado como AOT nativo. Idealmente, todas las aplicaciones que usan AOT nativo no deberían tener advertencias de AOT. Si hay advertencias de AOT, asegúrese de que no haya cambios en el funcionamiento probando exhaustivamente su aplicación tras compilarla como AOT nativo.
Ejemplos de advertencias de AOT
Para la mayoría del código de C#, es sencillo determinar qué código nativo debe generarse. El compilador nativo puede recorrer los cuerpos del método y encontrar el código nativo y las estructuras de datos a los que se accede. Desafortunadamente, algunas características, como la reflexión, presentan un problema importante. Observe el código siguiente:
Type t = typeof(int);
while (true)
{
t = typeof(GenericType<>).MakeGenericType(t);
Console.WriteLine(Activator.CreateInstance(t));
}
struct GenericType<T> { }
Aunque el programa anterior no es muy útil, representa un caso extremo que requiere que se cree un número infinito de tipos genéricos al compilar la aplicación como AOT nativo. Sin AOT nativo, el programa se ejecutaría hasta que se queda sin memoria. Con AOT nativo, ni siquiera podríamos compilarlo si fueramos a generar todos los tipos necesarios (el número infinito de ellos).
En este caso, la compilación con AOT nativo emite la siguiente advertencia en la línea 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.
En tiempo de ejecución, la aplicación sí producirá una excepción desde la llamada MakeGenericType
.
Reaccionar a las advertencias de AOT
Las advertencias de AOT están diseñadas para aportar predictibilidad a las compilaciones con AOT nativo. La mayoría de las advertencias de AOT se refieren a posibles excepciones en tiempo de ejecución en situaciones en las que no se generó código nativo para admitir el escenario. La categoría más amplia es RequiresDynamicCodeAttribute
.
RequireDynamicCode
RequiresDynamicCodeAttribute es sencillo y general: es un atributo que indica que el miembro ha sido anotado como incompatible con AOT. Esta anotación significa que el miembro puede usar la reflexión u otro mecanismo para crear código nativo en tiempo de ejecución. Este atributo se usa cuando el código no es compatible fundamentalmente con AOT o la dependencia nativa es demasiado compleja para predecir estáticamente en tiempo de compilación. Esto a menudo sería cierto para los métodos que utilizan la Type.MakeGenericType
API, emisión de reflexión o otras tecnologías de generación de código en tiempo de ejecución. El código siguiente muestra un ejemplo.
[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();
}
No hay muchas soluciones alternativas para RequiresDynamicCode
. La mejor solución consiste en no llamar al método bajo ningún concepto al crear aplicaciones con AOT nativo, sino usar otro sistema que sea compatible con AOT. Si está escribiendo una biblioteca y no está en su mano llamar o no al método, también puede añadir RequiresDynamicCode
a su propio método. Esto anotará el método como no compatible con AOT. Agregar RequiresDynamicCode
silenciará todas las advertencias de AOT en el método anotado, pero producirá una advertencia cada vez que otra persona lo llame. Por esta razón, la mayoría de las veces los autores de bibliotecas encuentran útil meter la advertencia "en una burbuja" en una API pública.
Si puede determinar de alguna manera que la llamada es segura y todo el código nativo estará disponible en tiempo de ejecución, también puede suprimir la advertencia mediante UnconditionalSuppressMessageAttribute. Por ejemplo:
[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
es como SuppressMessage
, pero lo pueden ver publish
y otras herramientas posteriores a la compilación. Las directivas SuppressMessage
y #pragma
solo están presentes en el origen, por lo que no se pueden usar para silenciar las advertencias de la compilación.
Precaución
Tenga cuidado al suprimir advertencias de AOT. La llamada podría ser compatible con AOT ahora, pero a medida que actualices el código, esto podría cambiar y puedas olvidar revisar todas las supresiones.