Native AOT 不支持反射的动态类型操作
Native AOT 在编译期必须确定所有类型和成员的布局,无法在运行时通过
Assembly.Load、
Type.GetType或
Activator.CreateInstance(string)动态加载或实例化未知类型。即使类型存在,只要路径是字符串形式(如
"MyApp.Foo"),AOT 就会报错或静默失败。
常见错误现象:
System.TypeLoadException: Could not load type或链接阶段直接失败(如
ILLink报
Unresolved assembly)。 允许:使用已知类型的强引用,如
typeof(Foo)、
new Foo()禁止:基于字符串的类型解析、
Assembly.GetExecutingAssembly().GetTypes()(除非显式保留) 折中方案:用
[DynamicDependency]或
rd.xml声明需保留的类型,但仅限编译期可枚举的集合
Native AOT 不支持 LINQ to Objects 的完整表达式树执行
AOT 无法在运行时编译
Expression<func bool>></func>为委托(即不支持
expr.Compile())。所有表达式必须能在编译期被
ILLink静态分析并内联,否则链接器会剪掉相关代码,导致
NotSupportedException。
使用场景:Entity Framework Core 的客户端求值、自定义查询构建器、运行时拼接条件表达式。
允许:list.Where(x => x.Id > 5)(编译为普通委托,非表达式树) 禁止:
Expression<func bool>> expr = x => x.Age > 18; var func = expr.Compile();</func>替代方式:改用预定义委托、策略模式或代码生成(如 Source Generator 输出静态方法)
Native AOT 不支持托管堆上的运行时代码生成
包括
Reflection.Emit(
AssemblyBuilder、
TypeBuilder)、
Delegate.CreateDelegate(部分重载)、以及依赖 JIT 的任何机制。AOT 输出的是纯原生机器码,没有运行时编译器。
典型踩坑点:某些序列化库(如旧版
Newtonsoft.Json的默认设置)、RPC 框架的代理生成、或手写 IL 织入逻辑。 允许:
Delegate.CreateDelegate(typeof(Func<int>), obj, "MethodName")</int>(目标方法必须已存在且可达) 禁止:
DynamicMethod、
ILGenerator、
RuntimeMethodHandle.GetFunctionPointer()兼容做法:用
System.Text.Json(AOT 友好)、或启用
JsonSerializerOptions.PreferredObjectCreationHandling = ObjectCreationHandling.Reuse避免反射构造
Native AOT 对泛型实例化的限制比 JIT 更严格
AOT 要求每个泛型闭包(如
List<string></string>、
Dictionary<int user></int>)在编译期明确出现过,否则不会为其生成本地代码。隐式泛型实例(如仅通过反射触发)会被剪掉。
性能影响:过度保守的泛型使用会导致二进制体积膨胀;过于激进的剪裁则引发运行时
MissingMethodException。 必须显式触发:在代码中写
new List<string>()</string>或标记
[RequiresUnreferencedCode]提示工具链保留 接口实现泛型类时,若实现类未被直接引用,需用
[DynamicDependency]关联
typeof(T).IsGenericType等反射检查仍可用,但不能据此决定是否实例化新泛型类型 AOT 的核心约束都源于“无运行时编译器”这一事实——所有类型、方法、委托绑定必须在链接前固化。最容易被忽略的是那些看似 innocuous 的反射调用,比如日志框架里根据命名空间自动扫描 Handler,或者配置系统里按字符串名查找转换器。这些逻辑在 JIT 下跑得欢,在 AOT 下往往悄无声息地失效。
