zoukankan      html  css  js  c++  java
  • 使用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

  • 相关阅读:
    20145204《信息安全系统设计基础》期中总结
    20145204&20145212信息安全系统实验一报告
    k8s运维记
    服务器免密登录
    非正常关闭vi编辑器产生swp文件怎么删除
    centos7 安装 python3 、docker、 docker-compose 脚本
    数据库高可用方案
    centos7安装docker-compose报错解决办法
    centos7 一键安装python3 --转发
    安装docker-compose的两种方式
  • 原文地址:https://www.cnblogs.com/kklldog/p/codedom.html
Copyright © 2011-2022 走看看