前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用CodeDom动态生成类型

使用CodeDom动态生成类型

作者头像
MJ.Zhou
发布2018-10-09 14:43:05
1K0
发布2018-10-09 14:43:05
举报
文章被收录于专栏:.NET开发那点事

.NET 3.5的时候加入了匿名类型这个特性,我们可以直接使用 new {name="abc"} 来直接生成一个对象。这个特性现在应用的地方很多,比如dapper的查询参数都是用匿名对象。 其实匿名对象也不是真的没有名称,编译器在编译后自动会生成一个Type。我们看看IL就知道了。

编译器会自动生成一个叫做<>f__AnonymousType0`1的类型。

动态生成类型

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

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

    public Person(string name ,int age)
    {
        this.name = name;
        this.age = age;
    }
}
构造类型
代码语言:javascript
复制
        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代码
代码语言:javascript
复制
        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代码

编译
代码语言:javascript
复制
        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

运行一下

代码语言:javascript
复制
        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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-10-29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动态生成类型
    • 构造类型
      • 生成CSharp代码
        • 编译
        • 运行一下
        • 参考
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档