C#如何动态生成实体类?5种方法详解与实战演示
摘要:本文介绍C#中动态生成实体类的5种实用方法,涵盖T4模板、CodeDOM、Roslyn、反射和Emit等技术,通过真实代码示例帮助开发者应对不同场景需求。
一、应用场景分析
动态生成实体类常用于:
-
数据库表结构变动时自动同步
-
动态解析JSON/XML等异构数据源
-
减少重复编码工作
-
运行时动态类型创建
二、实现方案对比
方法 | 易用性 | 灵活性 | 性能 | 适用阶段 |
---|---|---|---|---|
T4模板 | ★★★★ | ★★ | 高 | 设计时 |
CodeDOM | ★★★ | ★★★ | 中 | 设计/运行时 |
Roslyn API | ★★★ | ★★★★ | 中 | 运行时 |
Reflection.Emit | ★★ | ★★★★ | 高 | 运行时 |
第三方库 | ★★★★ | ★★★ | 中 | 运行时 |
三、具体实现方法
方法1:使用T4模板生成(设计时)
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#var className = "DynamicEntity";var properties = new Dictionary<string, string> {{"Id", "int"},{"Name", "string"}};
#>
// Auto-generated class
public class <#= className #>
{
<# foreach(var prop in properties) { #>public <#= prop.Value #> <#= prop.Key #> { get; set; }
<# } #>
}
优点:Visual Studio原生支持
缺点:需要预生成文件
方法2:使用CodeDOM(运行时)
var compileUnit = new CodeCompileUnit();
var @namespace = new CodeNamespace("DynamicEntities");
var @class = new CodeTypeDeclaration("Person") { IsClass = true };@class.Members.Add(new CodeMemberField(typeof(int), "_age"));
var property = new CodeMemberProperty()
{Name = "Age",Type = new CodeTypeReference(typeof(int)),Attributes = MemberAttributes.Public
};
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(null, "_age")));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(null, "_age"),new CodePropertySetValueReferenceExpression()));@class.Members.Add(property);
var provider = new CSharpCodeProvider();
using var writer = new StringWriter();
provider.GenerateCodeFromCompileUnit(compileUnit, writer, null);
File.WriteAllText("Person.cs", writer.ToString());
适用场景:需要生成完整类文件时
方法3:使用Roslyn API(运行时)
var syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
namespace DynamicEntities
{public class Employee{public string FirstName { get; set; }public decimal Salary { get; set; }}
}");var compilation = CSharpCompilation.Create("DynamicAssembly").AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)).AddSyntaxTrees(syntaxTree);using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (result.Success)
{var assembly = Assembly.Load(ms.ToArray());dynamic obj = Activator.CreateInstance(assembly.GetType("DynamicEntities.Employee"));obj.FirstName = "John";
}
优势:支持完整的编译流程
注意:需要安装Microsoft.CodeAnalysis.CSharp包
方法4:使用Reflection.Emit(高性能)
var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType("Product", TypeAttributes.Public);// 添加属性
var fieldBuilder = typeBuilder.DefineField("_price", typeof(decimal), FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty("Price", PropertyAttributes.None, typeof(decimal), null);// 生成get/set方法
var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName;
var getMethod = typeBuilder.DefineMethod("get_Price", getSetAttr, typeof(decimal), Type.EmptyTypes);
var getIL = getMethod.GetILGenerator();
getIL.Emit(OpCodes.Ldarg_0);
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
getIL.Emit(OpCodes.Ret);var setMethod = typeBuilder.DefineMethod("set_Price", getSetAttr, null, new[] { typeof(decimal) });
var setIL = setMethod.GetILGenerator();
setIL.Emit(OpCodes.Ldarg_0);
setIL.Emit(OpCodes.Ldarg_1);
setIL.Emit(OpCodes.Stfld, fieldBuilder);
setIL.Emit(OpCodes.Ret);propertyBuilder.SetGetMethod(getMethod);
propertyBuilder.SetSetMethod(setMethod);var dynamicType = typeBuilder.CreateType();
dynamic obj = Activator.CreateInstance(dynamicType);
obj.Price = 99.99m;
特点:最高性能,适合高频使用场景
四、注意事项
-
动态程序集无法卸载问题
-
类型冲突处理
-
调试困难建议添加异常处理
-
考虑使用缓存机制提升性能
五、方案选型建议
-
简单场景:选择T4模板
-
需要动态编译:使用Roslyn
-
高性能需求:优先Emit
-
快速开发:选择第三方库如Newtonsoft.Json
结语:根据项目需求选择合适方案,建议从T4模板开始熟悉,逐步掌握Emit等高级技巧。欢迎在评论区交流实际应用场景!
推荐工具:
-
LINQPad:快速测试动态代码
-
ILSpy:查看生成的IL代码
-
Microsoft.CodeAnalysis:Roslyn核心库