反射 Emit 动态类型生成
大约 14 分钟约 4133 字
反射 Emit 动态类型生成
简介
Reflection.Emit 是 .NET 提供的运行时代码生成技术,允许在程序运行时动态创建程序集、模块、类型、方法和 IL 代码。它常用于 ORM 框架、AOP 代理、序列化库等需要高性能动态调用的场景。相比反射调用,Emit 生成的代码接近原生性能。
在现代 .NET 中,Source Generator 已经在很多场景下替代了 Emit,但 Emit 仍然在纯运行时动态场景中不可替代。理解 Emit 的工作原理对于理解 .NET 运行时和框架设计至关重要。
特点
反射性能体系总览
反射 vs Emit vs 表达式树 vs Source Generator
// 性能对比(从慢到快):
// 1. MethodInfo.Invoke() — 最慢,每次都有大量安全检查和装箱
// 2. DynamicMethod / Emit — 快,生成委托后调用接近原生
// 3. Expression.Compile() — 快,编译后性能接近 Emit
// 4. 直接调用 — 最快,编译器优化最充分
// 5. Source Generator — 编译时生成代码,运行时等同直接调用
// 示例 — 四种属性访问方式对比
public class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
// 方式 1: 纯反射(最慢)
var prop = typeof(User).GetProperty("Name")!;
var value1 = prop.GetValue(user); // 内部多次安全检查
// 方式 2: Emit 生成的委托(快)
var getter = EmitHelper.CreateGetter<User, string>("Name");
var value2 = getter(user); // 委托调用,接近原生
// 方式 3: 表达式树编译(快)
var exprGetter = ExpressionHelper.BuildGetter<User>("Name");
var value3 = exprGetter(user); // 编译后的委托
// 方式 4: 直接调用(最快)
var value4 = user.Name; // JIT 可以内联动态程序集与类型
创建动态类型
/// <summary>
/// 使用 AssemblyBuilder 创建动态类型
/// </summary>
public static Type CreateDtoType(string typeName, Dictionary<string, Type> properties)
{
// 创建动态程序集
var assemblyName = new AssemblyName("DynamicAssembly");
var assembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("MainModule");
// 创建类型
var typeBuilder = module.DefineType(typeName,
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
// 为每个属性生成字段和 getter/setter
foreach (var (name, type) in properties)
{
var field = typeBuilder.DefineField($"_{name}", type, FieldAttributes.Private);
var property = typeBuilder.DefineProperty(name, PropertyAttributes.None, type, null);
// Getter
var getter = typeBuilder.DefineMethod($"get_{name}",
MethodAttributes.Public | MethodAttributes.SpecialName, type, null);
var il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldfld, field); // _field
il.Emit(OpCodes.Ret);
property.SetGetMethod(getter);
// Setter
var setter = typeBuilder.DefineMethod($"set_{name}",
MethodAttributes.Public | MethodAttributes.SpecialName, null, new[] { type });
var setterIl = setter.GetILGenerator();
setterIl.Emit(OpCodes.Ldarg_0); // this
setterIl.Emit(OpCodes.Ldarg_1); // value
setterIl.Emit(OpCodes.Stfld, field);
setterIl.Emit(OpCodes.Ret);
property.SetSetMethod(setter);
}
return typeBuilder.CreateType()!;
}创建带构造函数的完整类型
/// <summary>
/// 创建带构造函数、方法和事件的完整动态类型
/// </summary>
public static Type CreateCompleteType()
{
var assemblyName = new AssemblyName("CompleteDynamicAssembly");
var assembly = AssemblyBuilder.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("MainModule");
// 定义类型
var typeBuilder = module.DefineType("DynamicEntity",
TypeAttributes.Public | TypeAttributes.Class);
// 定义字段
var idField = typeBuilder.DefineField("_id", typeof(int), FieldAttributes.Private);
var nameField = typeBuilder.DefineField("_name", typeof(string), FieldAttributes.Private);
// ==========================================
// 定义构造函数
// ==========================================
var ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] { typeof(int), typeof(string) });
var ctorIl = ctor.GetILGenerator();
ctorIl.Emit(OpCodes.Ldarg_0); // this
ctorIl.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)!); // base()
ctorIl.Emit(OpCodes.Ldarg_0); // this
ctorIl.Emit(OpCodes.Ldarg_1); // id
ctorIl.Emit(OpCodes.Stfld, idField); // _id = id
ctorIl.Emit(OpCodes.Ldarg_0); // this
ctorIl.Emit(OpCodes.Ldarg_2); // name
ctorIl.Emit(OpCodes.Stfld, nameField); // _name = name
ctorIl.Emit(OpCodes.Ret);
// ==========================================
// 定义方法
// ==========================================
var toStringMethod = typeBuilder.DefineMethod("ToString",
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig,
typeof(string), Type.EmptyTypes);
var toStringIl = toStringMethod.GetILGenerator();
// 使用 String.Format
toStringIl.Emit(OpCodes.Ldstr, "DynamicEntity [Id={0}, Name={1}]");
toStringIl.Emit(OpCodes.Ldarg_0);
toStringIl.Emit(OpCodes.Ldfld, idField);
toStringIl.Emit(OpCodes.Box, typeof(int)); // 值类型装箱
toStringIl.Emit(OpCodes.Ldarg_0);
toStringIl.Emit(OpCodes.Ldfld, nameField);
toStringIl.Emit(OpCodes.Call, typeof(string).GetMethod(
"Format", new[] { typeof(string), typeof(object), typeof(object) })!);
toStringIl.Emit(OpCodes.Ret);
// 重写 Object.ToString
typeBuilder.DefineMethodOverride(toStringMethod, typeof(object).GetMethod("ToString")!);
// ==========================================
// 定义属性
// ==========================================
var idProperty = typeBuilder.DefineProperty("Id", PropertyAttributes.None, typeof(int), null);
var idGetter = typeBuilder.DefineMethod("get_Id",
MethodAttributes.Public | MethodAttributes.SpecialName, typeof(int), null);
var idGetterIl = idGetter.GetILGenerator();
idGetterIl.Emit(OpCodes.Ldarg_0);
idGetterIl.Emit(OpCodes.Ldfld, idField);
idGetterIl.Emit(OpCodes.Ret);
idProperty.SetGetMethod(idGetter);
// ==========================================
// 定义事件
// ==========================================
var eventField = typeBuilder.DefineField("_nameChanged",
typeof(EventHandler), FieldAttributes.Private);
var nameChangedEvent = typeBuilder.DefineEvent("NameChanged",
EventAttributes.None, typeof(EventHandler));
// add_NameChanged 方法
var addMethod = typeBuilder.DefineMethod("add_NameChanged",
MethodAttributes.Public | MethodAttributes.SpecialName,
null, new[] { typeof(EventHandler) });
var addIl = addMethod.GetILGenerator();
addIl.Emit(OpCodes.Ldarg_0);
addIl.Emit(OpCodes.Ldarg_1);
addIl.Emit(OpCodes.Call, typeof(Delegate).GetMethod("Combine",
new[] { typeof(Delegate), typeof(Delegate) })!);
addIl.Emit(OpCodes.Castclass, typeof(EventHandler));
addIl.Emit(OpCodes.Stfld, eventField);
addIl.Emit(OpCodes.Ret);
nameChangedEvent.SetAddOnMethod(addMethod);
return typeBuilder.CreateType()!;
}动态方法
创建高性能委托
/// <summary>
/// 用 Emit 快速创建属性访问委托
/// </summary>
public static Func<object, object> CreateGetter(PropertyInfo property)
{
var method = new DynamicMethod($"get_{property.Name}",
typeof(object), new[] { typeof(object) }, true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // 加载目标对象
il.Emit(OpCodes.Castclass, property.DeclaringType!); // 类型转换
il.EmitCall(OpCodes.Callvirt, property.GetMethod!, null); // 调用 getter
if (property.PropertyType.IsValueType)
il.Emit(OpCodes.Box, property.PropertyType); // 值类型装箱
il.Emit(OpCodes.Ret);
return (Func<object, object>)method.CreateDelegate(typeof(Func<object, object>));
}
// 使用
var getter = CreateGetter(typeof(User).GetProperty("Name")!);
object value = getter(userInstance); // 高性能,无反射开销泛型版高性能 Getter
/// <summary>
/// 泛型版 Getter — 避免装箱拆箱
/// </summary>
public static Func<T, TProperty> CreateTypedGetter<T, TProperty>(string propertyName)
{
var property = typeof(T).GetProperty(propertyName)
?? throw new ArgumentException($"属性 {propertyName} 不存在");
if (property.PropertyType != typeof(TProperty))
throw new ArgumentException($"属性类型不匹配");
var method = new DynamicMethod(
$"typed_get_{propertyName}",
typeof(TProperty),
new[] { typeof(T) },
restrictedSkipVisibility: true);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, property.GetMethod!);
il.Emit(OpCodes.Ret);
return (Func<T, TProperty>)method.CreateDelegate(
typeof(Func<T, TProperty>));
}
// 使用 — 零装箱
var nameGetter = CreateTypedGetter<User, string>("Name");
string name = nameGetter(user); // 无装箱,无反射,纯委托调用动态方法调用
/// <summary>
/// 用 Emit 代替 MethodInfo.Invoke 的高开销
/// </summary>
public static Action<object, object[]> CreateMethodInvoker(MethodInfo method)
{
var parameters = method.GetParameters();
var dm = new DynamicMethod("Invoker", null,
new[] { typeof(object), typeof(object[]) }, true);
var il = dm.GetILGenerator();
// 加载目标对象
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, method.DeclaringType!);
// 加载参数
for (int i = 0; i < parameters.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
var paramType = parameters[i].ParameterType;
if (paramType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, paramType);
}
else
{
il.Emit(OpCodes.Castclass, paramType);
}
}
il.EmitCall(OpCodes.Callvirt, method, null);
il.Emit(OpCodes.Ret);
return (Action<object, object[]>)dm.CreateDelegate(typeof(Action<object, object[]>));
}对象克隆器
/// <summary>
/// 用 Emit 实现高性能对象克隆
/// </summary>
public static Func<T, T> CreateCloner<T>() where T : new()
{
var type = typeof(T);
var method = new DynamicMethod($"clone_{type.Name}",
typeof(T), new[] { typeof(T) }, true);
var il = method.GetILGenerator();
// 创建新实例
il.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)!);
// 复制所有字段
foreach (var field in type.GetFields(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
il.Emit(OpCodes.Dup); // 复制新实例引用到栈上
il.Emit(OpCodes.Ldarg_0); // 加载源对象
il.Emit(OpCodes.Ldfld, field); // 加载源字段值
il.Emit(OpCodes.Stfld, field); // 设置目标字段值
}
il.Emit(OpCodes.Ret);
return (Func<T, T>)method.CreateDelegate(typeof(Func<T, T>));
}
// 使用
var cloner = CreateCloner<User>();
var clone = cloner(original); // 比反射克隆快 10-50 倍表达式树编译
用表达式树代替 Emit
/// <summary>
/// 表达式树 — 更高级的动态代码生成
/// </summary>
// 动态构建属性 getter
public static Func<T, object> BuildGetter<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "obj");
var property = Expression.Property(parameter, propertyName);
var convert = Expression.Convert(property, typeof(object));
return Expression.Lambda<Func<T, object>>(convert, parameter).Compile();
}
// 动态构建属性 setter
public static Action<T, object> BuildSetter<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "obj");
var value = Expression.Parameter(typeof(object), "value");
var property = Expression.Property(parameter, propertyName);
var convertedValue = Expression.Convert(value, property.Type);
var assignment = Expression.Assign(property, convertedValue);
return Expression.Lambda<Action<T, object>>(assignment, parameter, value).Compile();
}
// 使用
var getter = BuildGetter<User>("Name");
var setter = BuildSetter<User>("Name");
var user = new User { Name = "张三" };
setter(user, "李四");
Console.WriteLine(getter(user)); // "李四"表达式树高级应用 — 条件构建
/// <summary>
/// 动态构建 Where 条件(类似 EF Core 的动态查询)
/// </summary>
public static Func<T, bool> BuildPredicate<T>(
string propertyName, object value, string operation = "Equal")
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyName);
var constant = Expression.Constant(value, property.Type);
BinaryExpression comparison = operation switch
{
"Equal" => Expression.Equal(property, constant),
"NotEqual" => Expression.NotEqual(property, constant),
"GreaterThan" => Expression.GreaterThan(property, constant),
"LessThan" => Expression.LessThan(property, constant),
"GreaterThanOrEqual" => Expression.GreaterThanOrEqual(property, constant),
"LessThanOrEqual" => Expression.LessThanOrEqual(property, constant),
"Contains" => Expression.Call(
property,
typeof(string).GetMethod("Contains", new[] { typeof(string) })!,
constant),
_ => throw new NotSupportedException($"不支持的运算符: {operation}")
};
return Expression.Lambda<Func<T, bool>>(comparison, parameter).Compile();
}
// 使用
var predicate = BuildPredicate<User>("Age", 18, "GreaterThan");
var adults = users.Where(predicate).ToList();表达式树 — 动态对象映射
/// <summary>
/// 简化版 AutoMapper — 表达式树实现对象映射
/// </summary>
public static class ObjectMapper
{
private static readonly ConcurrentDictionary<(Type, Type), Func<object, object>>
_mappers = new();
public static Func<object, object> GetMapper(Type sourceType, Type targetType)
{
return _mappers.GetOrAdd((sourceType, targetType), key =>
BuildMapper(key.Item1, key.Item2));
}
private static Func<object, object> BuildMapper(Type sourceType, Type targetType)
{
var sourceParam = Expression.Parameter(typeof(object), "source");
var source = Expression.Convert(sourceParam, sourceType);
var newTarget = Expression.New(targetType);
// 查找所有匹配的属性
var sourceProps = sourceType.GetProperties();
var targetProps = targetType.GetProperties();
var bindings = new List<MemberBinding>();
foreach (var targetProp in targetProps)
{
var sourceProp = sourceProps.FirstOrDefault(p =>
p.Name == targetProp.Name &&
p.PropertyType == targetProp.PropertyType);
if (sourceProp != null)
{
var sourceAccess = Expression.Property(source, sourceProp);
bindings.Add(Expression.Bind(targetProp, sourceAccess));
}
}
var memberInit = Expression.MemberInit(newTarget, bindings);
var lambda = Expression.Lambda<Func<object, object>>(
Expression.Convert(memberInit, typeof(object)),
sourceParam);
return lambda.Compile();
}
}
// 使用
var mapper = ObjectMapper.GetMapper(typeof(UserDto), typeof(User));
var user = (User)mapper(userDto);Source Generator — 现代替代方案
为什么 Source Generator 更好
// Source Generator 在编译时生成代码,而不是运行时
// 优势:
// 1. 零运行时开销 — 生成的代码与手写代码完全相同
// 2. 调试友好 — 生成的代码可以查看和调试
// 3. 编译时检查 — 语法错误在编译时发现
// 4. AOT 兼容 — 生成的代码不需要动态生成能力
// 示例 — Source Generator 生成的映射代码
// 输入:
// [Mapper(typeof(Source), typeof(Destination))]
// partial class Mapper { }
// 输出(编译时生成):
/*
partial class Mapper
{
public static Destination Map(Source source)
{
return new Destination
{
Name = source.Name,
Age = source.Age,
// ...
};
}
}
*/
// 常见使用 Source Generator 的库:
// - AutoMapper.Extensions.Microsoft.DependencyInjection(源生成模式)
// - System.Text.Json 源生成
// - Microsoft.Extensions.DependencyInjection 源生成
// - Mapster
// - RSCG (Runtime Source Code Generation) 工具集System.Text.Json 源生成
// 传统反射序列化(运行时)
var json = JsonSerializer.Serialize(user);
// 源生成序列化(编译时)
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(List<User>))]
public partial class UserJsonContext : JsonSerializerContext
{
}
// 使用 — 零反射
var json = JsonSerializer.Serialize(user, UserJsonContext.Default.User);
var user = JsonSerializer.Deserialize<User>(json, UserJsonContext.Default.User);
// 性能优势:
// - 不需要运行时反射扫描类型
// - AOT 兼容(Native AOT 可以使用)
// - 启动时间更快
// - 内存分配更少IL 指令速查
常用 IL 指令
// ==========================================
// 加载指令(Load)
// ==========================================
// Ldarg_0 — 加载第 0 个参数(this)
// Ldarg_1 — 加载第 1 个参数
// Ldarg_S N — 加载第 N 个参数(短格式)
// Ldloc_0 — 加载局部变量 0
// Ldloc_S N — 加载局部变量 N
// Ldnull — 加载 null
// Ldstr "str" — 加载字符串常量
// Ldc_I4 N — 加载 int32 常量
// Ldc_I4_0~8 — 加载 0~8 的快速指令
// Ldfld field — 加载实例字段
// Ldsfld field — 加载静态字段
// Ldelem_Ref — 加载数组元素
// ==========================================
// 存储指令(Store)
// ==========================================
// Stloc_0 — 存储到局部变量 0
// Stloc_S N — 存储到局部变量 N
// Stsfld field — 存储到静态字段
// Stfld field — 存储到实例字段
// Stelem_Ref — 存储到数组元素
// Pop — 丢弃栈顶值
// ==========================================
// 方法调用
// ==========================================
// Call method — 调用方法(静态/实例/虚方法都可以)
// Callvirt method — 虚方法调用(会做 null 检查)
// Newobj ctor — 创建新对象
// Calli — 间接调用
// ==========================================
// 类型转换
// ==========================================
// Castclass type — 引用类型转换(可能抛异常)
// Isinst type — 安全类型转换(null 表示失败)
// Box type — 值类型装箱
// Unbox type — 拆箱获取引用
// Unbox_Any type — 拆箱并复制值
// Conv_I4 — 转换为 int32
// ==========================================
// 控制流
// ==========================================
// Ret — 返回
// Br label — 无条件跳转
// Brtrue label — 非零/非null 时跳转
// Brfalse label — 零/null 时跳转
// Leave label — 从 try 块中退出
// Throw — 抛出异常
// ==========================================
// 比较指令
// ==========================================
// Ceq — 相等比较
// Cgt — 大于比较
// Clt — 小于比较生产环境注意事项
动态程序集的内存管理
// AssemblyBuilderAccess 决定了动态程序集的生命周期
// Run — 程序集在 AppDomain 卸载前一直存在(默认)
// RunAndCollect — 程序集可以被 GC 回收
// RunAndSave — 程序集可以保存到磁盘
// Save — 仅保存到磁盘,不加载到内存
// 可回收的动态程序集(防止内存泄漏)
var assembly = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("TempAssembly"),
AssemblyBuilderAccess.RunAndCollect);
// 注意:RunAndCollect 的程序集在被 GC 回收前,
// 所有引用它的类型和委托也必须不可达Emit 代码的调试技巧
// 1. 使用 DebugInfoGenerator 在调试时生成调试信息
var assembly = AssemblyBuilder.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule(
"DebugModule", "DebugModule.pdb", emitSymbolInfo: true);
// 2. 使用 SOS 扩展查看动态类型
// 在 WinDbg 中:
// !dumpheap -type DynamicType
// !dumpobj 0x... 查看对象
// !dumpil 0x... 查看方法的 IL
// 3. 使用 MetadataLoadContext 检查生成的程序集
using var context = new MetadataLoadContext(
new PathAssemblyResolver(new[] { assembly.Location }));
// 4. 记录生成的 IL 用于调试
void DumpIL(DynamicMethod method)
{
// 获取 IL 字节码
var ilBytes = method.GetDynamicILInfo()?.GetILAsByteArray();
Console.WriteLine($"IL 长度: {ilBytes?.Length ?? 0} 字节");
}优点
缺点
总结
Reflection.Emit 适合框架和库的底层开发,普通业务代码建议使用表达式树(更安全、更可读)。Emit 核心场景:动态 DTO 生成、高性能属性访问器、AOP 代理生成。能用表达式树解决的不用 Emit,必须极致性能时再用 Emit。在现代 .NET 中,优先考虑 Source Generator(编译时生成),其次表达式树,最后 Emit(运行时生成)。
技术选型建议:
- 编译时已知类型 — 直接调用或 Source Generator
- 运行时需要动态构建表达式 — 表达式树(Expression Tree)
- 需要极致性能且不能用 Source Generator — Emit
- 只需要偶尔调用 — 反射 + 缓存即可
- AOT 场景 — 只能用 Source Generator 或预编译委托
关键知识点
- 先明确这个主题影响的是语法层、运行时层,还是性能与可维护性层。
- 学习时要同时关注语言表面写法和编译器、JIT、GC 等底层行为。
- 真正有价值的是知道"为什么这样写"和"在什么边界下不能这样写"。
项目落地视角
- 把示例改成最小可运行样例,并观察编译输出、运行结果和异常行为。
- 如果它会进入团队代码规范,最好同步补充命名约定、禁用场景和替代方案。
- 涉及性能结论时,优先用 Benchmark 或实际热点链路验证,而不是凭感觉判断。
常见误区
- 只记语法糖,不知道底层成本。
- 把适用于小样例的写法直接搬到高并发或大对象场景里。
- 忽略框架版本、语言版本和运行时差异,导致结论失真。
- 用 Emit 解决本可以用表达式树或 Source Generator 解决的问题。
- 忘记处理值类型的装箱拆箱,导致性能回退。
- 动态程序集没有正确释放,导致内存泄漏。
- Emit 生成的 IL 缺少栈平衡检查,导致运行时 VerificationException。
进阶路线
- 继续向源码、IL、JIT 行为和 BCL 实现层深入。
- 把知识点和代码评审、性能诊断、面试复盘结合起来。
- 把同类主题做横向对比,例如值类型与引用类型、迭代器与 async 状态机、反射与 Source Generator。
- 研究 Dapper、FastMember 等开源库的 Emit 实现。
- 学习编写自定义 Source Generator。
适用场景
- 当你准备把《反射 Emit 动态类型生成》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合在需要理解语言特性、运行时行为或 API 边界时阅读。
- 当代码开始出现性能瓶颈、可维护性问题或语义歧义时,这类主题会直接影响实现质量。
落地建议
- 先写最小可运行样例,再把结论迁移到真实业务代码。
- 同时记录这个特性的收益、限制和替代方案,避免为了"高级"而使用。
- 涉及内存、并发或序列化时,最好配合调试器或基准测试验证。
排错清单
- 先确认问题属于编译期、运行期还是语义误用。
- 检查是否存在隐式转换、装箱拆箱、闭包捕获或上下文切换等隐藏成本。
- 查看异常栈、日志和最小复现代码,优先排除使用姿势问题。
复盘问题
- 如果把《反射 Emit 动态类型生成》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《反射 Emit 动态类型生成》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《反射 Emit 动态类型生成》最大的收益和代价分别是什么?
