使用CodeDom动态生成类型
发布日期:2021-05-09 04:12:30 浏览次数:14 分类:博客文章

本文共 5266 字,大约阅读时间需要 17 分钟。

.NET 3.5的时候加入了匿名类型这个特性,我们可以直接使用 new {name="abc"} 来直接生成一个对象。这个特性现在应用的地方很多,比如dapper的查询参数都是用匿名对象。

其实匿名对象也不是真的没有名称,编译器在编译后自动会生成一个Type。我们看看IL就知道了。
编译器会自动生成一个叫做<>f__AnonymousType0`1的类型。

动态生成类型

但是有的时候我们可能类型里面的字段都是不确定的,这个时候我们就需要去动态生成一个类型了。

  • 动态生成类型第一个想到的就是反射,但是仔细想想反射都是基于现有Type的基础上完成的,咱们现在连Type都没有,所以这条路不通。
  • 第二个dynamic,dynamic确实是个好办法,可以动态指定字段的名称,但是有的三方的库不支持比如dapper。
  • 最后CodeDom,CodeDom可以在运行时直接生成一个Type。CodeDom生成Type主要分成3步。
    比如我们要生成一个Person类:
public class Person{    public string name;    public ing age;    public Person(string name ,int age)    {        this.name = name;        this.age = age;    }}

构造类型

private string _ns = "__x";        private string _className;        private Dictionary
_fieldsDictionary; private string _sourceCode; private CodeCompileUnit _targetUnit; private CodeTypeDeclaration _targetClass; public SourceCodeCreater(string className,Dictionary
fieldsDictionary ) { _fieldsDictionary = fieldsDictionary; _className = className; _targetUnit = new CodeCompileUnit(); CodeNamespace ns = new CodeNamespace(_ns); ns.Imports.Add(new CodeNamespaceImport("System")); _targetClass = new CodeTypeDeclaration(className); _targetClass.IsClass = true; _targetClass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed; ns.Types.Add(_targetClass); _targetUnit.Namespaces.Add(ns); } public string SourceCode { get { return _sourceCode; } } public string TypeName { get { return string.Format("{0}.{1}", _ns, _className); } } private void AddFields() { // Declare fields . foreach (var kv in _fieldsDictionary) { CodeMemberField widthValueField = new CodeMemberField(); widthValueField.Attributes = MemberAttributes.Public; widthValueField.Name = kv.Value; widthValueField.Type = new CodeTypeReference(kv.Key); _targetClass.Members.Add(widthValueField); } } private void AddCtor() { // Declare constructor CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final; // Add parameters. foreach (var kv in _fieldsDictionary) { constructor.Parameters.Add(new CodeParameterDeclarationExpression( kv.Key, kv.Value)); } // Add field initialization logic foreach (var kv in _fieldsDictionary) { CodeFieldReferenceExpression reference = new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), kv.Value); constructor.Statements.Add(new CodeAssignStatement(reference, new CodeArgumentReferenceExpression(kv.Value))); } _targetClass.Members.Add(constructor); }

我们按照手写类的结构添加字段跟构造函数。

生成CSharp代码

public string Create()        {            AddFields();            AddCtor();            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");            CodeGeneratorOptions options = new CodeGeneratorOptions();            options.BracingStyle = "C";            using (StringWriter sourceWriter = new StringWriter())            {                provider.GenerateCodeFromCompileUnit(                    _targetUnit, sourceWriter, options);                _sourceCode = sourceWriter.ToString();            }            return _sourceCode;        }

生成CSharp代码

编译

SourceCodeCreater sourceCodeCreater =new SourceCodeCreater(className,fields);        var sourceCode = sourceCodeCreater.Create();        Console.WriteLine(sourceCode);        var typeName = sourceCodeCreater.TypeName;        CSharpCodeProvider p = new CSharpCodeProvider();        CompilerParameters param = new CompilerParameters(new string[] { "System.dll" });        CompilerResults rel = p.CompileAssemblyFromSource(param, sourceCode);        Type t = rel.CompiledAssembly.GetType(typeName);

编译代码获得Type

运行一下

static void Main(string[] args)        {            var className = "Person";            var fields =new Dictionary
(); fields.Add(typeof(string),"name"); fields.Add(typeof(int),"age"); SourceCodeCreater sourceCodeCreater =new SourceCodeCreater(className,fields); var sourceCode = sourceCodeCreater.Create(); Console.WriteLine(sourceCode); var typeName = sourceCodeCreater.TypeName; CSharpCodeProvider p = new CSharpCodeProvider(); CompilerParameters param = new CompilerParameters(new string[] { "System.dll" }); CompilerResults rel = p.CompileAssemblyFromSource(param, sourceCode); Type t = rel.CompiledAssembly.GetType(typeName); Console.WriteLine(t.FullName); foreach (var f in t.GetFields()) { Console.WriteLine("Type:{0} Name:{1}",f.FieldType,f.Name); } Console.Read(); }

参考

上一篇:.Net Core 自定义配置源从配置中心读取配置
下一篇:ASP.NET Core Blazor Webassembly 之 组件

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2025年04月05日 13时23分57秒