C#動(dòng)態(tài)生成實(shí)體類的5種方法詳解與實(shí)戰(zhàn)演示
摘要:本文介紹C#中動(dòng)態(tài)生成實(shí)體類的5種實(shí)用方法,涵蓋T4模板、CodeDOM、Roslyn、反射和Emit等技術(shù),通過真實(shí)代碼示例幫助開發(fā)者應(yīng)對(duì)不同場景需求。
一、應(yīng)用場景分析
動(dòng)態(tài)生成實(shí)體類常用于:
數(shù)據(jù)庫表結(jié)構(gòu)變動(dòng)時(shí)自動(dòng)同步
動(dòng)態(tài)解析JSON/XML等異構(gòu)數(shù)據(jù)源
減少重復(fù)編碼工作
運(yùn)行時(shí)動(dòng)態(tài)類型創(chuàng)建
二、實(shí)現(xiàn)方案對(duì)比
方法 | 易用性 | 靈活性 | 性能 | 適用階段 |
---|---|---|---|---|
T4模板 | ★★★★ | ★★ | 高 | 設(shè)計(jì)時(shí) |
CodeDOM | ★★★ | ★★★ | 中 | 設(shè)計(jì)/運(yùn)行時(shí) |
Roslyn API | ★★★ | ★★★★ | 中 | 運(yùn)行時(shí) |
Reflection.Emit | ★★ | ★★★★ | 高 | 運(yùn)行時(shí) |
第三方庫 | ★★★★ | ★★★ | 中 | 運(yùn)行時(shí) |
三、具體實(shí)現(xiàn)方法
方法1:使用T4模板生成(設(shè)計(jì)時(shí))
<#@ 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; } <# } #> }
優(yōu)點(diǎn):Visual Studio原生支持
缺點(diǎn):需要預(yù)生成文件
方法2:使用CodeDOM(運(yùn)行時(shí))
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());
適用場景:需要生成完整類文件時(shí)
方法3:使用Roslyn API(運(yùn)行時(shí))
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"; }
優(yōu)勢:支持完整的編譯流程
注意:需要安裝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;
特點(diǎn):最高性能,適合高頻使用場景
四、注意事項(xiàng)
動(dòng)態(tài)程序集無法卸載問題
類型沖突處理
調(diào)試?yán)щy建議添加異常處理
考慮使用緩存機(jī)制提升性能
五、方案選型建議
簡單場景:選擇T4模板
需要?jiǎng)討B(tài)編譯:使用Roslyn
高性能需求:優(yōu)先Emit
快速開發(fā):選擇第三方庫如Newtonsoft.Json
結(jié)語:根據(jù)項(xiàng)目需求選擇合適方案,建議從T4模板開始熟悉,逐步掌握Emit等高級(jí)技巧。歡迎在評(píng)論區(qū)交流實(shí)際應(yīng)用場景!
推薦工具:
LINQPad:快速測試動(dòng)態(tài)代碼
ILSpy:查看生成的IL代碼
Microsoft.CodeAnalysis:Roslyn核心庫
到此這篇關(guān)于C#動(dòng)態(tài)生成實(shí)體類的5種方法詳解與實(shí)戰(zhàn)演示的文章就介紹到這了,更多相關(guān)C#動(dòng)態(tài)生成實(shí)體類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
DevExpress實(shí)現(xiàn)自定義GridControl中按鈕文字內(nèi)容的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)自定義GridControl中按鈕文字內(nèi)容的方法,需要的朋友可以參考下2014-08-08WPF仿三星手機(jī)充電界面實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了WPF仿三星手機(jī)充電界面實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08C#利用SharpPcap實(shí)現(xiàn)網(wǎng)絡(luò)包捕獲嗅探
這篇文章主要為大家詳細(xì)介紹了C#利用SharpPcap實(shí)現(xiàn)網(wǎng)絡(luò)包捕獲嗅探,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03C# 在PDF文檔中創(chuàng)建表格的實(shí)現(xiàn)方法
表格能夠一目了然的讓用戶看到數(shù)據(jù)信息,使信息顯得有條理化,那么在pdf類型的文檔中如何來添加表格并對(duì)表格進(jìn)行格式化操作呢?下面小編給大家?guī)砹薈# 在PDF文檔中創(chuàng)建表格的實(shí)現(xiàn)方法,需要的朋友參考下吧2017-12-12通過C#實(shí)現(xiàn)發(fā)送自定義的html格式郵件
本篇文章主要介紹了通過C#實(shí)現(xiàn)發(fā)送自定義的html格式郵件,詳細(xì)的介紹了發(fā)送HTML格式郵件的方法,有興趣的可以了解一下。2017-02-02C#實(shí)現(xiàn)時(shí)間戳與標(biāo)準(zhǔn)時(shí)間的互轉(zhuǎn)
本文主要介紹了C#中時(shí)間戳與標(biāo)準(zhǔn)時(shí)間互轉(zhuǎn)的方法,其中需要注意的是基準(zhǔn)時(shí)間的問題。文中的示例代碼具有一定的學(xué)習(xí)價(jià)值,快來跟隨小編一起了解一下吧2021-12-12