如何从存储过程返回的类定义字符串动态生成C#类/Type?
从类定义字符串动态生成Type的实现方案
当然可以做到!在C#中,我们可以通过动态编译技术把类定义字符串转换成可直接使用的Type对象,下面给你两种实用的实现方式,分别适配不同的.NET环境:
方案一:使用CSharpCodeProvider(适用于.NET Framework)
这是.NET Framework时代常用的动态编译方式,不需要额外安装NuGet包,直接用系统自带的类库:
using System; using System.CodeDom.Compiler; using System.Reflection; using Microsoft.CSharp; public static Type GenerateType(string classDefinition) { // 补全必要的代码结构:添加using和命名空间,避免编译错误 var fullCode = $@" using System; namespace DynamicTypes {{ {classDefinition} }}"; // 设置编译参数 var compilerParams = new CompilerParameters { GenerateInMemory = true, // 在内存中生成程序集,不写入磁盘 ReferencedAssemblies = { "System.dll" } // 引用必要的程序集 }; // 创建C#编译器 using var provider = new CSharpCodeProvider(); var compileResult = provider.CompileAssemblyFromSource(compilerParams, fullCode); // 检查编译错误 if (compileResult.Errors.HasErrors) { var errorMsg = string.Join(Environment.NewLine, compileResult.Errors.Cast<CompilerError>().Select(e => $"Error: {e.ErrorText}")); throw new InvalidOperationException($"编译失败:{errorMsg}"); } // 从生成的程序集中获取Type(注意类名要和传入的定义一致) var assembly = compileResult.CompiledAssembly; return assembly.GetType($"DynamicTypes.{GetClassName(classDefinition)}"); } // 辅助方法:从类定义字符串中提取类名 private static string GetClassName(string classDefinition) { // 简单的提取逻辑,你可以根据实际情况优化(比如处理public、partial等修饰符) var startIndex = classDefinition.IndexOf("class ") + 6; var endIndex = classDefinition.IndexOf(" ", startIndex); if (endIndex == -1) endIndex = classDefinition.IndexOf("{", startIndex); return classDefinition.Substring(startIndex, endIndex - startIndex).Trim(); }
使用示例:
var classStr = "public class TableName { public int Column1 { get; set; } }"; Type dynamicType = GenerateType(classStr); // 后续可以用反射创建实例、访问属性 object instance = Activator.CreateInstance(dynamicType); PropertyInfo column1Prop = dynamicType.GetProperty("Column1"); column1Prop.SetValue(instance, 123); Console.WriteLine(column1Prop.GetValue(instance)); // 输出123
方案二:使用Roslyn API(推荐,适用于.NET Core/.NET 5+)
Roslyn是微软官方的.NET编译器平台,功能更强大、性能更好,需要先安装NuGet包Microsoft.CodeAnalysis.CSharp:
using System; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Reflection; public static Type GenerateTypeWithRoslyn(string classDefinition) { // 补全完整代码结构 var fullCode = $@" using System; namespace DynamicTypes {{ {classDefinition} }}"; // 构建语法树 var syntaxTree = CSharpSyntaxTree.ParseText(fullCode); // 设置编译选项:引用必要的程序集 var references = new MetadataReference[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; var compilation = CSharpCompilation.Create( "DynamicAssembly", syntaxTrees: new[] { syntaxTree }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); // 编译到内存流 using var ms = new System.IO.MemoryStream(); var result = compilation.Emit(ms); // 检查编译错误 if (!result.Success) { var errors = result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error); var errorMsg = string.Join(Environment.NewLine, errors.Select(d => $"Error: {d.GetMessage()}")); throw new InvalidOperationException($"编译失败:{errorMsg}"); } // 加载程序集并获取Type ms.Seek(0, System.IO.SeekOrigin.Begin); var assembly = Assembly.Load(ms.ToArray()); return assembly.GetType($"DynamicTypes.{GetClassName(classDefinition)}"); } // 复用之前的GetClassName辅助方法 private static string GetClassName(string classDefinition) { var startIndex = classDefinition.IndexOf("class ") + 6; var endIndex = classDefinition.IndexOf(" ", startIndex); if (endIndex == -1) endIndex = classDefinition.IndexOf("{", startIndex); return classDefinition.Substring(startIndex, endIndex - startIndex).Trim(); }
注意事项
- 类定义字符串要保证语法正确,否则编译会失败,建议添加必要的错误处理逻辑。
- 如果你的类需要引用其他程序集(比如
System.ComponentModel等),要在编译参数中添加对应的引用。 - 生成的
Type可以通过反射进行所有常规操作:创建实例、调用方法、访问属性等。
内容的提问来源于stack exchange,提问作者Ankur Arora
相关产品推荐
相关产品推荐

