iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0

動態能力與反射

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);
    }
}

上一篇
JIT / R2R / Native AOT 差異與實測
下一篇
PGO 與性能
系列文
新 .NET & Azure & IoT & AI 開源技術實戰手冊 (含深入官方程式碼講解) 16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言