动态方法调用(DynamicMethod)
定义
命名空间:TouchSocket.Core
程序集:TouchSocket.Core.dll
一、核心概念
动态方法调用模块提供高效、灵活的方法反射调用方案,支持多种底层实现方式,显著提升反射调用性能。特别针对AOT(Ahead-of-Time)编译环境优化,同时保持传统反射场景的高性能表现。
二、核心特性
- 多引擎支持:
- IL代码生成(DynamicBuilderType.IL)
- 表达式树(DynamicBuilderType.Expression)
- 传统反射(DynamicBuilderType.Reflect)
- 源生成(DynamicBuilderType.SourceGenerator)
- 性能卓越:相比原生反射调用,性能提升10倍性能
- AOT友好:源生成模式实现零反射,完美支持iOS/Android等AOT环境
- 智能异步支持:自动识别Task/ValueTask返回值类型
- 全参数支持:支持ref/out参数、泛型参数、参数默认值
- 灵活扩展:支持自定义动态方法特性标记
三、快速开始
3.1 基本声明
public class MyClass
{
[DynamicMethod]
public void SimpleMethod()
{
Console.WriteLine("Method executed");
}
}
3.2 基础调用
// 创建方法包装器
var method = new Method(typeof(MyClass), nameof(MyClass.SimpleMethod));
// 实例化对象
var instance = new MyClass();
// 执行方法调用
method.Invoke(instance);
四、核心功能详解
4.1 构建器类型选择
// 使用IL生成
var ilMethod = new Method(
typeof(MyClass),
nameof(MyClass.SimpleMethod),
DynamicBuilderType.IL
);
// 使用表达式树
var exprMethod = new Method(
typeof(MyClass),
nameof(MyClass.SimpleMethod),
DynamicBuilderType.Expression
);
// 使用源生成(性能最强,AOT环境推荐)
var sourceGenMethod = new Method(
typeof(MyClass),
nameof(MyClass.SimpleMethod),
DynamicBuilderType.SourceGenerator
);
构建器选择建议
- 任何时候:使用SourceGenerator,无论性能还是AOT环境,都是首选。
4.2 异步方法支持
public class MyClass
{
[DynamicMethod]
public async Task<int> GetDataAsync()
{
await Task.Delay(100);
return 42;
}
}
// 异步调用
var method = new Method(typeof(MyClass), nameof(MyClass.GetDataAsync));
var result = await method.InvokeAsync(instance);
Console.WriteLine($"Result: {result}"); // 输出 42
4.3 复杂参数处理
public class MyClass
{
[DynamicMethod]
public void ProcessData(
string input,
ref int counter,
out string result)
{
counter++;
result = $"{input}_{counter}";
}
}
// 调用示例
var parameters = new object[] { "data", 0, null };
method.Invoke(instance, parameters);
Console.WriteLine($"Result: {parameters[2]}"); // 输出 "data_1"
五、性能优化
5.1 性能对比测试
10000次调用性能分析
核心结论
-
同步方法(Add)性能
- 直接调用 最快(~2.1µs),无内存分配。
- 源生成器(SourceGeneratorRun) 在 .NET 8+ 接近直接调用性能(~3.1µs),较 IL/表达式树快 6-7 倍,较反射快 28-30 倍。
- 反射(MethodInfo) 性能最差(.NET 6: 400µs → .NET 8: 88µs),.NET 8+ 反射性能显著优 化。
-
异步方法(AddAsync)性能
- 直接调用 仍最优(.NET 8: 38µs,.NET 9: 39µs)。
- 源生成器 表现接近 IL/表达式树(.NET 8: 91µs vs 103µs),较反射快 2-3 倍(.NET 8: 91µs vs 184µs)。
- .NET Framework 4.8.1 异步性能最差(反射达 1,305µs),内存分配显著高于其他版本。
-
内存分配
- 同步方法:源生成器与 IL/表达式树分配 80B,反射额外多 1B(.NET 6)。
- 异步方法:所有动态方法分配 ~720KB(.NET Core)或 ~802KB(Framework),反射在 Framework 分配超 1MB。
附:关键数据对比(.NET 8)
方法 | 同步耗时 | 异步耗时 | 内存分配 |
---|---|---|---|
DirectRun | 2.1µs | 38µs | 56B |
SourceGeneratorRun | 3.1µs | 91µs | 80B |
MethodInfoRun | 88µs | 184µs | 80B |
| Method | Job | Runtime | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|----------------------------- |--------------------- |--------------------- |-------------:|----------:|----------:|---------:|-------:|----------:|
| DirectRun_Add | .NET 6.0 | .NET 6.0 | 2.132 us | 0.0031 us | 0.0027 us | - | - | 56 B |
| MethodILRun_Add | .NET 6.0 | .NET 6.0 | 23.887 us | 0.0795 us | 0.0744 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 6.0 | .NET 6.0 | 20.108 us | 0.1542 us | 0.1443 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 6.0 | .NET 6.0 | 399.999 us | 0.3781 us | 0.3537 us | - | - | 81 B |
| SourceGeneratorRun_Add | .NET 6.0 | .NET 6.0 | 17.648 us | 0.0945 us | 0.0884 us | - | - | 80 B |
| DirectRun_AddAsync | .NET 6.0 | .NET 6.0 | 46.567 us | 0.4623 us | 0.4098 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 6.0 | .NET 6.0 | 141.010 us | 0.8145 us | 0.7619 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 6.0 | .NET 6.0 | 142.758 us | 0.8197 us | 0.7266 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 6.0 | .NET 6.0 | 563.645 us | 1.8681 us | 1.6560 us | 45.8984 | - | 720081 B |
| SourceGeneratorRun_AddAsync | .NET 6.0 | .NET 6.0 | 141.399 us | 0.8781 us | 0.8214 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 6.0 | .NET 6.0 | 2.130 us | 0.0018 us | 0.0017 us | - | - | 56 B |
| DirectRun_Add | .NET 8.0 | .NET 8.0 | 2.140 us | 0.0116 us | 0.0109 us | - | - | 56 B |
| MethodILRun_Add | .NET 8.0 | .NET 8.0 | 19.332 us | 0.0491 us | 0.0459 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 8.0 | .NET 8.0 | 12.889 us | 0.1189 us | 0.1054 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 8.0 | .NET 8.0 | 88.879 us | 0.1784 us | 0.1581 us | - | - | 80 B |
| SourceGeneratorRun_Add | .NET 8.0 | .NET 8.0 | 3.160 us | 0.0073 us | 0.0068 us | 0.0038 | - | 80 B |
| DirectRun_AddAsync | .NET 8.0 | .NET 8.0 | 38.401 us | 0.1199 us | 0.1063 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 8.0 | .NET 8.0 | 103.627 us | 1.2387 us | 1.1587 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 8.0 | .NET 8.0 | 101.368 us | 0.7190 us | 0.6725 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 8.0 | .NET 8.0 | 183.882 us | 0.7238 us | 0.6044 us | 45.8984 | - | 720080 B |
| SourceGeneratorRun_AddAsync | .NET 8.0 | .NET 8.0 | 91.024 us | 0.6482 us | 0.6063 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 8.0 | .NET 8.0 | 2.150 us | 0.0073 us | 0.0068 us | - | - | 56 B |
| DirectRun_Add | .NET 9.0 | .NET 9.0 | 2.119 us | 0.0024 us | 0.0022 us | - | - | 56 B |
| MethodILRun_Add | .NET 9.0 | .NET 9.0 | 19.348 us | 0.0133 us | 0.0124 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET 9.0 | .NET 9.0 | 11.753 us | 0.0625 us | 0.0585 us | - | - | 80 B |
| MethodInfoRun_Add | .NET 9.0 | .NET 9.0 | 91.756 us | 0.2344 us | 0.2193 us | - | - | 80 B |
| SourceGeneratorRun_Add | .NET 9.0 | .NET 9.0 | 3.145 us | 0.0056 us | 0.0046 us | 0.0038 | - | 80 B |
| DirectRun_AddAsync | .NET 9.0 | .NET 9.0 | 39.219 us | 0.1669 us | 0.1561 us | 45.8984 | - | 720080 B |
| MethodILRun_AddAsync | .NET 9.0 | .NET 9.0 | 107.920 us | 0.4728 us | 0.4191 us | 45.8984 | - | 720080 B |
| MethodExpressionRun_AddAsync | .NET 9.0 | .NET 9.0 | 106.539 us | 0.3894 us | 0.3642 us | 45.8984 | - | 720080 B |
| MethodInfoRun_AddAsync | .NET 9.0 | .NET 9.0 | 189.294 us | 0.7231 us | 0.6764 us | 45.8984 | - | 720080 B |
| SourceGeneratorRun_AddAsync | .NET 9.0 | .NET 9.0 | 99.195 us | 0.3677 us | 0.3440 us | 45.8984 | - | 720080 B |
| StaticMethodRun | .NET 9.0 | .NET 9.0 | 2.125 us | 0.0036 us | 0.0034 us | - | - | 56 B |
| DirectRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 2.134 us | 0.0047 us | 0.0042 us | 0.0076 | - | 56 B |
| MethodILRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 30.162 us | 0.0629 us | 0.0588 us | - | - | 80 B |
| MethodExpressionRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 56.063 us | 0.0559 us | 0.0467 us | - | - | 80 B |
| MethodInfoRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 770.931 us | 5.9446 us | 5.5606 us | 50.7813 | - | 321027 B |
| SourceGeneratorRun_Add | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 17.521 us | 0.0350 us | 0.0310 us | - | - | 80 B |
| DirectRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 41.866 us | 0.1300 us | 0.1216 us | 127.5024 | 0.0610 | 802442 B |
| MethodILRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 523.507 us | 2.1568 us | 1.9120 us | 126.9531 | - | 802445 B |
| MethodExpressionRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 554.333 us | 2.3657 us | 2.2129 us | 126.9531 | - | 802445 B |
| MethodInfoRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 1,304.826 us | 1.4392 us | 1.2758 us | 177.7344 | - | 1123389 B |
| SourceGeneratorRun_AddAsync | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 508.533 us | 0.8991 us | 0.8410 us | 126.9531 | - | 802445 B |
| StaticMethodRun | .NET Framework 4.8.1 | .NET Framework 4.8.1 | 3.267 us | 0.0070 us | 0.0062 us | 0.0114 | - | 80 B |
基准测试代码
[SimpleJob(RuntimeMoniker.Net481)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net90)]
[MemoryDiagnoser]
public class BenchmarkInvokeMethodClass
{
public BenchmarkInvokeMethodClass()
{
var methodInfo_Add = typeof(MyMethodClass1).GetMethod(nameof(MyMethodClass1.Add));
var methodInfo_AddAsync = typeof(MyMethodClass1).GetMethod(nameof(MyMethodClass1.AddAsync));
this.m_method_Add_IL = new Method(methodInfo_Add, DynamicBuilderType.IL);
this.m_method_Add_Expression = new Method(methodInfo_Add, DynamicBuilderType.Expression);
this.m_method_Add_SourceGenerator = new Method(methodInfo_Add, DynamicBuilderType.SourceGenerator);
this.m_method_Add_Reflect = new Method(methodInfo_Add, DynamicBuilderType.Reflect);
this.m_method_AddAsync_IL = new Method(methodInfo_AddAsync, DynamicBuilderType.IL);
this.m_method_AddAsync_Expression = new Method(methodInfo_AddAsync, DynamicBuilderType.Expression);
this.m_method_AddAsync_SourceGenerator = new Method(methodInfo_AddAsync, DynamicBuilderType.SourceGenerator);
this.m_method_AddAsync_Reflect = new Method(methodInfo_AddAsync, DynamicBuilderType.Reflect);
var AddStatic = typeof(MyMethodClass1).GetProperty("AddStaticAction");
}
private readonly Method m_method_Add_IL;
private readonly Method m_method_Add_Expression;
private readonly Method m_method_Add_SourceGenerator;
private readonly Method m_method_Add_Reflect;
private readonly Method m_method_AddAsync_IL;
private readonly Method m_method_AddAsync_Expression;
private readonly Method m_method_AddAsync_SourceGenerator;
private readonly Method m_method_AddAsync_Reflect;
public int Count=10000;
[Benchmark]
public void DirectRun_Add()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
myClass1.Add(a);
}
}
[Benchmark]
public void MethodILRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_IL.Invoke(myClass2, objects);
}
}
[Benchmark]
public void MethodExpressionRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_Expression.Invoke(myClass2, objects);
}
}
[Benchmark]
public void MethodInfoRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_Reflect.Invoke(myClass2, objects);
}
}
[Benchmark]
public void SourceGeneratorRun_Add()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
this.m_method_Add_SourceGenerator.Invoke(myClass2, objects);
}
}
[Benchmark]
public async Task DirectRun_AddAsync()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await myClass1.AddAsync(a);
}
}
[Benchmark]
public async Task MethodILRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_IL.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task MethodExpressionRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_Expression.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task MethodInfoRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_Reflect.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public async Task SourceGeneratorRun_AddAsync()
{
var myClass2 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
await this.m_method_Add_SourceGenerator.InvokeAsync(myClass2, objects);
}
}
[Benchmark]
public void StaticMethodRun()
{
var myClass1 = new MyMethodClass1();
var a = new object();
var objects = new object[] { a };
for (var i = 0; i < this.Count; i++)
{
MyMethodClass1.AddStatic(myClass1, objects);
}
}
}
public class MyMethodClass1
{
public static object AddStatic(object myClass, object[] ps)
{
var a = ps[0];
var result = ((MyMethodClass1)myClass).Add(a);
return result;
}
//这个是被调用函数。
[DynamicMethod]
public object Add(object obj)
{
return obj;
}
[DynamicMethod]
public Task<object> AddAsync(object obj)
{
return Task.FromResult(obj);
}
}
5.2 缓存策略
// 推荐在应用初始化时预加 载方法
public static class MethodCache
{
public static readonly Method ProcessMethod = new Method(
typeof(MyClass),
nameof(MyClass.ProcessData),
DynamicBuilderType.IL
);
}
// 后续重复使用缓存实例
MethodCache.ProcessMethod.Invoke(instance);
六、高级用法
6.1 自定义特性标记
// 定义自定义特性
[AttributeUsage(AttributeTargets.Method)]
public class MyDynamicAttribute : DynamicMethodAttribute {}
// 标记方法
public class CustomService
{
[MyDynamic]
public void CustomOperation() { }
}
// 获取自定义标记方法
var methods = typeof(CustomService)
.GetMethods()
.Where(m => m.IsDefined(typeof(MyDynamicAttribute)));