如何在C#中创建带动态字段与属性的动态类作为数据模型
在C#中创建带动态字段的数据模型类
下面提供三种实用方案,覆盖从快速实现到强类型生成的不同需求场景:
方案1:用ExpandoObject快速实现动态属性
适合简单场景,无需强类型,直接动态添加属性:
using System.Dynamic; public dynamic CreateDynamicModel(params string[] fields) { dynamic expando = new ExpandoObject(); var expandoDict = (IDictionary<string, object>)expando; foreach (var field in fields) { if (!expandoDict.ContainsKey(field)) { expandoDict[field] = null; // 初始值可按需修改为默认值 } } return expando; } // 使用示例 var model = CreateDynamicModel("Name", "Age", "Email"); model.Name = "张三"; model.Age = 25; Console.WriteLine(model.Name); // 输出:张三
注意:这种方式本质是字典包装,编译时无属性检查,适合快速验证或简单数据传递场景。
方案2:通过反射Emit生成强类型动态类
如果需要真正可复用的.NET类型(比如用于序列化、ORM映射),可以用反射在运行时生成类:
using System; using System.Reflection; using System.Reflection.Emit; public static Type CreateDynamicClass(params string[] propertyNames) { // 定义动态程序集和模块 var assemblyName = new AssemblyName($"DynamicModels_{Guid.NewGuid()}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule"); // 定义类结构 var typeBuilder = moduleBuilder.DefineType($"DynamicModel_{Guid.NewGuid()}", TypeAttributes.Public | TypeAttributes.Class); // 为每个字段生成属性 foreach (var propName in propertyNames) { // 生成私有字段 var fieldBuilder = typeBuilder.DefineField($"_{propName}", typeof(object), FieldAttributes.Private); // 生成属性的get方法 var getMethod = typeBuilder.DefineMethod($"get_{propName}", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(object), Type.EmptyTypes); var getIL = getMethod.GetILGenerator(); getIL.Emit(OpCodes.Ldarg_0); getIL.Emit(OpCodes.Ldfld, fieldBuilder); getIL.Emit(OpCodes.Ret); // 生成属性的set方法 var setMethod = typeBuilder.DefineMethod($"set_{propName}", MethodAttributes.Public | MethodAttributes.SpecialName, typeof(void), new[] { typeof(object) }); var setIL = setMethod.GetILGenerator(); setIL.Emit(OpCodes.Ldarg_0); setIL.Emit(OpCodes.Ldarg_1); setIL.Emit(OpCodes.Stfld, fieldBuilder); setIL.Emit(OpCodes.Ret); // 绑定get/set方法到属性 var propBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.None, typeof(object), null); propBuilder.SetGetMethod(getMethod); propBuilder.SetSetMethod(setMethod); } // 生成并返回类型 return typeBuilder.CreateType(); } // 使用示例 var dynamicType = CreateDynamicClass("Id", "Title", "Content"); var instance = Activator.CreateInstance(dynamicType); // 设置属性值 dynamicType.GetProperty("Title").SetValue(instance, "动态类示例"); // 获取属性值 var title = dynamicType.GetProperty("Title").GetValue(instance); Console.WriteLine(title); // 输出:动态类示例
这种方式生成的是标准.NET类型,支持反射、序列化等所有常规操作,适合需要强类型模型的场景。
方案3:用Roslyn编译生成自定义类
如果需要更复杂的类定义(比如添加特性、实现接口),可以用Roslyn在运行时编译C#代码:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.IO; using System.Linq; public static Type CreateDynamicClassWithRoslyn(params string[] propertyNames) { // 构造类的代码模板 var properties = string.Join("\n ", propertyNames.Select(name => $"public object {name} {{ get; set; }}")); var code = $@" namespace DynamicModels {{ public class GeneratedModel {{ {properties} }} }}"; // 编译代码 var syntaxTree = CSharpSyntaxTree.ParseText(code); var references = AppDomain.CurrentDomain.GetAssemblies() .Where(a => !a.IsDynamic && !string.IsNullOrEmpty(a.Location)) .Select(a => MetadataReference.CreateFromFile(a.Location)) .ToList(); var compilation = CSharpCompilation.Create($"DynamicAssembly_{Guid.NewGuid()}") .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddReferences(references) .AddSyntaxTrees(syntaxTree); using var ms = new MemoryStream(); var result = compilation.Emit(ms); if (!result.Success) { var errors = result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error); throw new InvalidOperationException($"编译失败:{string.Join("\n", errors.Select(e => e.GetMessage()))}"); } ms.Seek(0, SeekOrigin.Begin); var assembly = Assembly.Load(ms.ToArray()); return assembly.GetType("DynamicModels.GeneratedModel"); } // 使用示例 var roslynType = CreateDynamicClassWithRoslyn("Name", "Address", "Phone"); var roslynInstance = Activator.CreateInstance(roslynType); roslynType.GetProperty("Phone").SetValue(roslynInstance, "13800138000"); Console.WriteLine(roslynType.GetProperty("Phone").GetValue(roslynInstance)); // 输出:13800138000
需要先通过NuGet安装Microsoft.CodeAnalysis.CSharp包,这种方式灵活性极高,几乎支持所有C#类的语法特性。
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

