Общие сведения о предупреждениях AOT
При публикации приложения в качестве собственного AOT процесс сборки создает все структуры машинного кода и данных, необходимые для поддержки приложения во время выполнения. Это отличается от неродных развертываний, которые выполняют приложение от форматов, описывающих приложение абстрактными терминами (программа для виртуальной машины) и создавая собственные представления по запросу во время выполнения.
Абстрактное представление частей программы не имеет сопоставления "один к одному" с собственным представлением. Например, абстрактное описание универсального List<T>.Add
метода сопоставляется с потенциально бесконечными собственными телами методов, которые должны быть специализированы для данного T
(например, List<int>.Add
и List<double>.Add
).
Поскольку связь абстрактного кода с машинным кодом не является одно-одной, процесс сборки должен создать полный список тел и структур данных машинного кода во время сборки. Это может быть сложно создать во время сборки для некоторых API .NET. Если API используется таким образом, который не ожидался во время сборки, исключение будет создано во время выполнения.
Чтобы предотвратить изменения в поведении при развертывании в качестве собственного AOT, пакет SDK для .NET предоставляет статический анализ совместимости AOT с помощью предупреждения AOT. Предупреждения AOT создаются при обнаружении сборки кода, который может быть несовместим с AOT. Код, несовместимый с AOT, может привести к изменению поведения или даже сбою в приложении после его создания в качестве собственного AOT. В идеале все приложения, использующие собственный AOT, не должны иметь предупреждений AOT. Если есть предупреждения AOT, убедитесь, что нет изменений в поведении, тщательно протестируя приложение после создания в качестве собственного AOT.
Примеры предупреждений AOT
Для большинства кода C# просто определить, какой машинный код необходимо создать. Собственный компилятор может ходить по телам методов и находить доступ к каким структурам машинного кода и данных. К сожалению, некоторые функции, такие как отражение, представляют серьезную проблему. Рассмотрим следующий код:
Type t = typeof(int);
while (true)
{
t = typeof(GenericType<>).MakeGenericType(t);
Console.WriteLine(Activator.CreateInstance(t));
}
struct GenericType<T> { }
Хотя указанная выше программа не очень полезна, она представляет собой крайний случай, требующий бесконечного числа универсальных типов при создании приложения в качестве собственного AOT. Без собственного AOT программа будет работать до тех пор, пока не закончится память. С помощью собственного AOT мы не смогли бы даже построить его, если бы мы могли создать все необходимые типы (бесконечное число из них).
В этом случае сборка Native AOT выдает следующее предупреждение в строке 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.
Во время выполнения приложение действительно вызовет исключение MakeGenericType
.
Реагирование на предупреждения AOT
Предупреждения AOT предназначены для обеспечения прогнозируемости сборок AOT в машинном коде. Большинство предупреждений AOT относятся к возможному исключению во время выполнения в ситуациях, когда машинный код не был создан для поддержки сценария. Самая широкая категория .RequiresDynamicCodeAttribute
ТребуетсяDynamicCode
RequiresDynamicCodeAttribute простой и широкий: это атрибут, который означает, что член был аннотирован как несовместимый с AOT. Эта заметка означает, что член может использовать отражение или другой механизм для создания нового машинного кода во время выполнения. Этот атрибут используется, если код принципиально не совместим с AOT, или собственная зависимость слишком сложна для статического прогнозирования во время сборки. Это часто верно для методов, использующих Type.MakeGenericType
API, отражение и другие технологии создания кода во время выполнения. Следующий код показывает пример.
[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();
}
Решений для RequiresDynamicCode
немного. Лучшее исправление заключается в том, чтобы избежать вызова метода во всех случаях при создании в качестве собственного AOT и использовать что-то другое, совместимое с AOT. Если вы пишете библиотеку и не можете решать, вызывать метод или нет, можете также добавить RequiresDynamicCode
в свой собственный метод. При этом метод будет заметен как несовместимый с AOT. Добавление RequiresDynamicCode
молчания всех предупреждений AOT в аннотированный метод, но будет создавать предупреждение всякий раз, когда кто-то другой вызывает его. По этой причине в основном полезно авторам библиотеки "пузырька" предупреждения к общедоступному API.
Если вы можете каким-то образом определить, что вызов является безопасным, и весь машинный код будет доступен во время выполнения, вы также можете отключить предупреждение с помощью UnconditionalSuppressMessageAttribute. Например:
[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
похож на SuppressMessage
, но видим для publish
и других средств после сборки. SuppressMessage
и #pragma
директивы присутствуют только в источнике, поэтому их нельзя использовать для молчания предупреждений сборки.
Внимание
Будьте осторожны при подавлении предупреждений AOT. Вызов может быть совместим с AOT сейчас, но при обновлении кода, который может измениться, и вы можете забыть просмотреть все подавления.