JIT 與 R2R 完整支援 Reflection.Emit、動態產生委派、Expression Tree 編譯、序列化庫System.Text.Json 自動生成執行期模型。Native AOT 對 DynamicMethod、Assembly.Load、反射掃描、未標記的序列化成員都有限制,需以 rd.xml、DynamicDependencyAttribute、RequiresUnreferencedCodeAttribute 標註保存。熱插拔如 Razor runtime 編譯不適合 Native AOT。需要 runtime code generation 或大型第三方 ORM時建議保留 JIT/R2R。
像是以下例子:
Native AOT 限制:Reflection.Emit 特別是動態組件、動態型別無法使用。
要遷移時以來源產生器預先產生對應程式碼加上 Dictionary 才能使用。
using System;
using System.Reflection;
using System.Reflection.Emit;
public static class DynamicEmitExample
{
public static void Run()
{
var asmName = new AssemblyName("MyDynamicAssembly");
// 在 JIT/R2R 情境下允許 AssemblyBuilderAccess.Run
var asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
var module = asmBuilder.DefineDynamicModule("MainMod");
var typeBuilder = module.DefineType("Calc", TypeAttributes.Public);
var method = typeBuilder.DefineMethod("Add", MethodAttributes.Public | MethodAttributes.Static,
typeof(int), new[] { typeof(int), typeof(int) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);
var calcType = typeBuilder.CreateType();
var mi = calcType.GetMethod("Add");
Console.WriteLine(mi.Invoke(null, new object[] { 5, 7 })); // 12
}
}
DynamicMethod 建立輕量委派 Native AOT 中通常不可行
using System;
using System.Reflection.Emit;
public static class DynamicMethodExample
{
public static void Run()
{
var dm = new DynamicMethod("Mul", typeof(int), new[] { typeof(int), typeof(int) });
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Mul);
il.Emit(OpCodes.Ret);
var del = (Func<int,int,int>)dm.CreateDelegate(typeof(Func<int,int,int>));
Console.WriteLine(del(3, 4)); // 12
}
}
想要 AOT 支持,建議用靜態泛型 + switch 或事先註冊委派替代
using System;
using System.Collections.Generic;
public static class PredeclaredDelegates
{
private static readonly Dictionary<string, Delegate> _map = new()
{
["Mul"] = (Func<int,int,int>)((a,b) => a * b),
["Add"] = (Func<int,int,int>)((a,b) => a + b),
};
public static T Get<T>(string opName) where T : Delegate
=> (T)_map[opName];
}
Expression Tree 動態編譯跟預編策略需要注意的點
在動態編譯 JIT OK,Native AOT 若使用 Compile() 也會受限
using System;
using System.Linq.Expressions;
public static class ExpressionTreeExample
{
public static void Run()
{
ParameterExpression a = Expression.Parameter(typeof(int), "a");
ParameterExpression b = Expression.Parameter(typeof(int), "b");
var body = Expression.Add(a, b);
var lambda = Expression.Lambda<Func<int,int,int>>(body, a, b);
var compiled = lambda.Compile(); // AOT 會有不允許 runtime emit 情況
Console.WriteLine(compiled(10, 20));
}
}
可以改成只在建置期間產生或改用手寫委派映射
using System;
using System.Collections.Generic;
public static class ExpressionPrecomputed
{
private static readonly Func<int,int,int> Add = (x,y) => x + y;
// 可由 Source Generator 自動產生該區塊
public static int Execute(string op, int a, int b)
=> op switch
{
"Add" => Add(a,b),
_ => throw new NotSupportedException()
};
}
最後關於我們 .NET 工程師最常用的 System.Text.Json,Runtime JIT OK,Native AOT 需保留型別 metadata。
我們通常寫法
using System.Text.Json;
public class User { public int Id {get;set;} public string Name {get;set;} = ""; }
public static class JsonRuntime
{
public static void Run()
{
var u = new User { Id = 1, Name = "Alice" };
string json = JsonSerializer.Serialize(u);
var u2 = JsonSerializer.Deserialize<User>(json);
}
}
Source Generator 的 AOT 推薦作法
public class User { public int Id {get;set;} public string Name {get;set;} = ""; }
public class Order { public int Id {get;set;} public decimal Amount {get;set;} }
using System.Text.Json.Serialization;
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(Order))]
public partial class MyJsonContext : JsonSerializerContext
{
}
using System.Text.Json;
public static class JsonUseSourceGen
{
public static void Run()
{
var user = new User { Id = 42, Name = "Bob" };
string json = JsonSerializer.Serialize(user, MyJsonContext.Default.User);
var back = JsonSerializer.Deserialize(json, MyJsonContext.Default.User);
}
}