使用CodeDom动态生成类型

.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<Type, string> _fieldsDictionary;

        private string _sourceCode;

        private CodeCompileUnit _targetUnit;
        private CodeTypeDeclaration _targetClass;
        public SourceCodeCreater(string className,Dictionary<Type,string> 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<Type,string>();
            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();
        }

参考

MSDN CodeDom

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

EF 通用数据层父类方法小结

注:该数据层方法为小弟平时项目所用方法,特此贡献出来,各位如有疑问或者好的建议可以提出来,大家一起成长!!!

751
来自专栏技术之路

用linqPad帮助你快速学习LINQ

在这里我向大家推荐的一个具是LinqPad有了这个工具并熟练使用就可以很快学习并掌握linq linqPad下载地址:http://www.linqpad.ne...

2056
来自专栏草根专栏

使用 dynamic 类型让 ASP.NET Core 实现 HATEOAS 结构的 RESTful API

上一篇写的是使用静态基类方法的实现步骤: http://www.cnblogs.com/cgzl/p/8726805.html 使用dynamic (Expa...

5226
来自专栏技术博客

编写高质量代码改善C#程序的157个建议[10-12]

  本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

934
来自专栏大内老A

我的WCF之旅(4):WCF中的序列化[下篇]

XMLSerializer 提到XMLSerializer,我想绝大多数人都知道这是asmx采用的Serializer。首先我们还是来看一个例子,通过比较Ma...

21710
来自专栏菩提树下的杨过

Silverlight中的序列化

序列化简言之是这样一种能力:能够把复杂的对象(Object)变成某种格式的字符串(常见的格式有xml,string,二进制文件等),这样可以方便的在各种系统中传...

1965
来自专栏跟着阿笨一起玩NET

C# 常用日期类型转换帮助类

本文转载:http://www.cnblogs.com/iamlilinfeng/p/3378659.html

2792
来自专栏cnblogs

async 和 await 之异步编程的学习

      async修改一个方法,表示其为异步方法。而await表示等待一个异步任务的执行。js方面,在es7中开始得以支持;而.net在c#5.0开始支持。...

2018
来自专栏飞扬的花生

C#List的排序和简单去重总结

      List集合在开发过程中很常见,经常我们要对该集合进行一系列操作,本文介绍如何将该集合内的元素进行排序,博主制作简单WinForm应用程序进行演示。...

1919
来自专栏葡萄城控件技术团队

最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary

本文对常用的数据结构详述:Array, ArrayList,List,IList,ICollection, Stack, Queue, HashTable, D...

2698

扫码关注云+社区

领取腾讯云代金券