摘要:本文介绍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核心库
到此这篇关于c#动态生成实体类的5种方法详解与实战演示的文章就介绍到这了,更多相关c#动态生成实体类内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论