发新话题
打印

[原创] DotNet探密 之: CodeDOM [代码文档对象模型]

[原创] DotNet探密 之: CodeDOM [代码文档对象模型]

DotNet探密之:闲言                                       
        使用Microsoft .NET已经有些时候了,越深入的研究就越加发觉Microsoft .NET的一些有趣的特性,时常会想,                                                               
大家知道它的这些语言特性吗,是不是也象我一样,在MSDN.NET的星空里寻找散落的繁星呢?有感于此,                                                                       
所以决定把自己的一些学习心得共享,第一次写这么长的技术文章,估计有点罗嗦,欢迎大家拍砖斧正^-^                                                                       
                                [原创] DotNet探密 之: CodeDOM [代码文档对象模型]                                       
一:什么是CodeDOM和CodeDOM可以实现的功能:                                                                       
        MSDN.NET2003中是这样描叙CodeDOM的:“CodeDOM 提供了表示许多常见的源代码元素类型的类型。您可以设计                                                               
一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。可以使用受支持的编程语言的 CodeDOM 代码生成器,                                                                       
将该对象图呈现为源代码。CodeDOM也可以用于将源代码编译成二进制程序集。”                                                                       
        这个不但是CodeDOM的定义,也说明了CodeDom功用:                                                               
        <一>:提供了表示许多常见的源代码元素类型的类型。                                                               
        <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。                                                               
        <三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。                                                               
        <四>:CodeDOM也可以用于将源代码编译成二进制程序集。                                                               
        我们来一一分析一下:                                                               
        “<一>:提供了表示许多常见的源代码元素类型的类型。” :大家知道“常见的源代码元素类型”在.NET这样完全面       
向对象的框架里,无非就是指名称空间/类/方法/属性了吧,那么“源代码元素类型的类型”又是什么呢?               
        “<二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。”:刚才说的是源代码元素类型       
的类型,那么这个是不是说可以根据这些类型之间的关系来生成对象图呢?是的话,又如何实现的呢?               
        “<三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。”:这里主要提到了可以       
通过CodeDOM 代码生成器将生成的对象图呈现为源代码,并且可以生成所支持的源代码,比如说C#,比如说VB.NET,               
如果Delphi.NET支持了,那么也可以生成。               
        “<四>:CodeDOM也可以用于将源代码编译成二进制程序集。”:意思是说可以通过CodeDOM来编译运行所生成的       
源代码,那么CodeDOM如何实现的呢?               
        带着上面的疑问,我们进入下一节,慢慢来体会CodeDOM的精髓:       

二:CodeDOM中表示源代码元素的类型及其应用:               
        因为CodeDOM中有对应常见源代码元素类型的类型,所以CodeDOM应该拥有庞大的对象群。而正是它庞大的对象群,       
常让设计开发人员有种无从下手的感觉,但是一旦了解了,就会发现其实这些对象的用法还是相当直观的。               
        I:CodeCompileUnit 和 CodeNamespace:       
CodeCompileUnit是CodeDOM中的根容器型对象,它可以加入CodeNamespace来生成namespace语句。如下程序演示了此用法。               
运用CodeCompileUnit对象的例子:                       
        using System;               
        using System.IO;               
        using System.CodeDom;               
        using System.CodeDom.Compiler;               

        namespace TestCodeDOM_01               
        {               

                class TestCodeDOM       
                {       

                        [STAThread]
                        static void Main(string[] args)
                        {

                                CodeCompileUnit CompileUnit = CodeDOMTest();

                                StringWriter SWriter = null;
                                SWriter = GetCodeDOMSource( CompileUnit, "CSharp" );
                                //SWriter = GetCodeDOMSource( CompileUnit, "VisualBasic" );

                                Console.ReadLine();
                        }       

                        /*       
                         * CodeDOM测试函数:       
                         * 演示 <一>:提供了表示许多常见的源代码元素类型的类型。       
                         * 演示 <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。       
                         */       
                        private static CodeCompileUnit CodeDOMTest()       
                        {       
                                CodeCompileUnit CompileUnit = new CodeCompileUnit();       
                                CodeNamespace   CNameSpace= new CodeNamespace("CodeDOMSpace");       
                                try       
                                {       
                                        CompileUnit.Namespaces.Add( CNameSpace);
                                }       
                                catch       
                                {       
                                        throw new Exception("CodeDOM出错!");
                                }       

                                return CompileUnit;       
                        }               

                        /*               
                         * C根据CodeCompileUnit生成源代码,并且放到StringWriter中:               
                         * 演示  <三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。                       
                         */                       
                        private static StringWriter GetCodeDOMSource(CodeCompileUnit CompileUnit, String sProviderType)                       
                        {                       
                                StringWriter SWriter = new StringWriter();               

                                CodeDomProvider CProvider = null;               

                                switch ( sProviderType )               
                                {               
                                        case "CSharp" :       
                                                CProvider = new Microsoft.CSharp.CSharpCodeProvider();
                                                break;
                                        case "VisualBasic" :       
                                                CProvider = new Microsoft.VisualBasic.VBCodeProvider();
                                                break;
                                        default:                               
                                                throw new Exception( "参数错误!" );                       
                                }                                       

                                ICodeGenerator Generator = CProvider.CreateGenerator();                                       
                                Generator.GenerateCodeFromCompileUnit( CompileUnit,SWriter,new CodeGeneratorOptions() );                                       
                                Console.Write( SWriter.ToString() );                                       
                                return SWriter;                                       
                                                                               
                        }                                               
                }                                                       
        }                                                               
运行结果就不输出了,大家可以自己去看看。该程序还演示了CodeDOM的                                                                       
        <一>:提供了表示许多常见的源代码元素类型的类型。                                                               
        <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。                                                               
        <三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。                                                               
是怎么实现的。                                                       

        II:CodeNamespaceImport 和 CodeTypeDeclaration:                                               
CodeNamespaceImport 对象用来生成namespace引用的代码: using XXX。但是2003下的.NET框架只支持名称空间下的namespace                                                       
引用。也就是说只允许来加入CodeNamespace来加入CodeNamespaceImport.                                                       
CodeTypeDeclaration是处理类、结构或枚举的类型声明的对象,它也只能由CodeNamespace来加入。                                                       
        我们修改如上给出代码的CodeDOMTest()函数,别的代码不变:                                               
        /*                                               
         * CodeDOM测试函数:                                               
         * 演示 <一>:提供了表示许多常见的源代码元素类型的类型。                                               
         * 演示 <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。                                               
         */                                               
        private static CodeCompileUnit CodeDOMTest()                                               
        {                                               
                CodeCompileUnit CompileUnit = new CodeCompileUnit();                                       
                CodeNamespace   CNameSpace = new CodeNamespace("CodeDOMSpace");                                       
                try       
                {       
                        //新添加的代码Begin
                        CNameSpace.Imports.Add(new CodeNamespaceImport("System"));
                        CNameSpace.Imports.Add(new CodeNamespaceImport("System.IO"));

                        CodeTypeDeclaration CClass = new CodeTypeDeclaration( "CodeDOMClass" );
                        CNameSpace.Types.Add( CClass );

                        CodeTypeDeclaration CInterFace = new CodeTypeDeclaration( "CodeDOMInterFace" );
                        CInterFace.IsInterface = true;
                        CNameSpace.Types.Add( CInterFace );
                        //新添加的代码End
                        CompileUnit.Namespaces.Add( CNameSpace );

                }       
                catch                               
                {                               
                        throw new Exception("CodeDOM出错!");                       
                }                               

                return CompileUnit;                               
        }                                       

        类型当然需要指定了,如下代码演示了如何实现抽象类和定义该抽象类为protected:                                       
        CodeTypeDeclaration CAbstractClass = new CodeTypeDeclaration( "CodeDOMAbstractClass" );                                       
        CAbstractClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract |                                       
                                                System.Reflection.TypeAttributes.Public;

        CNameSpace.Types.Add( CAbstractClass );                                       
        如下为所有System.Reflection.TypeAttributes的可能值:                                       
        public enum System.Reflection.TypeAttributes                                        
        {
            Abstract = 0x00000080,
            AnsiClass = 0x00000000,
            AutoClass = 0x00020000,
            AutoLayout = 0x00000000,
            BeforeFieldInit = 0x00100000,
            Class = 0x00000000,
            ClassSemanticsMask = 0x00000020,
            ExplicitLayout = 0x00000010,
            HasSecurity = 0x00040000,
            Import = 0x00001000,
            Interface = 0x00000020,
            LayoutMask = 0x00000018,
            NestedAssembly = 0x00000005,
            NestedFamANDAssem = 0x00000006,
            NestedFamily = 0x00000004,
            NestedFamORAssem = 0x00000007,
            NestedPrivate = 0x00000003,
            NestedPublic = 0x00000002,
            NotPublic = 0x00000000,
            Public = 0x00000001,
            ReservedMask = 0x00040800,
            RTSpecialName = 0x00000800,
            Sealed = 0x00000100,
            SequentialLayout = 0x00000008,
            Serializable = 0x00002000,
            SpecialName = 0x00000400,
            StringFormatMask = 0x00030000,
            UnicodeClass = 0x00010000,
            VisibilityMask = 0x00000007,
        }
        如下代码演示了如何定义类型的父类:
        CClass.BaseTypes.Add( new CodeTypeReference( typeof(System.IO.File) ) );               
        至此,CodeDOMTest()函数变为了如下:               
        using System;               
        using System.IO;               
        using System.CodeDom;               
        using System.CodeDom.Compiler;               

        namespace TestCodeDOM_01               
        {               

                class TestCodeDOM       
                {       

                        [STAThread]
                        static void Main(string[] args)
                        {

                                CodeCompileUnit CompileUnit = CodeDOMTest();

                                StringWriter SWriter = null;
                                SWriter = GetCodeDOMSource( CompileUnit, "CSharp" );
                                //SWriter = GetCodeDOMSource( CompileUnit, "VisualBasic" );

                                Console.ReadLine();
                        }       

                        /*       
                         * CodeDOM测试函数:       
                         * 演示 <一>:提供了表示许多常见的源代码元素类型的类型。       
                         * 演示 <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。       
                         */       
                        private static CodeCompileUnit CodeDOMTest()       
                        {               
                                CodeCompileUnit CompileUnit = new CodeCompileUnit();       
                                CodeNamespace   CNameSpace = new CodeNamespace("CodeDOMSpace");       
                                try       
                                {       

                                        CNameSpace.Imports.Add(new CodeNamespaceImport("System"));
                                        CNameSpace.Imports.Add(new CodeNamespaceImport("System.IO"));

                                        CodeTypeDeclaration CClass = new CodeTypeDeclaration( "CodeDOMClass" );
                                        CClass.BaseTypes.Add( new CodeTypeReference( typeof(System.IO.File) ) );
                                        CNameSpace.Types.Add( CClass );

                                        CodeTypeDeclaration CInterFace = new CodeTypeDeclaration( "CodeDOMInterFace" );
                                        CInterFace.IsInterface = true;
                                        CNameSpace.Types.Add( CInterFace );

                                        CodeTypeDeclaration CAbstractClass = new CodeTypeDeclaration( "CodeDOMAbstractClass" );                                                               
                                        CAbstractClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract |                                                               
                                                                                                        System.Reflection.TypeAttributes.Public;
                                        CNameSpace.Types.Add( CAbstractClass );                                                               

                                        CompileUnit.Namespaces.Add( CNameSpace );                                                               

                                }                                                                       
                                catch                                                                       
                                {                                                                       
                                        throw new Exception("CodeDOM出错!");                                                               
                                }                                                                       

                                return CompileUnit;                                                                       
                        }                                                                               

                        /*                       
                         * C根据CodeCompileUnit生成源代码,并且放到StringWriter中:                       
                         * 演示  <三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。                       
                         */                       
                        private static StringWriter GetCodeDOMSource( CodeCompileUnit CompileUnit, String sProviderType )                       
                        {                       
                                StringWriter SWriter = new StringWriter();               

                                CodeDomProvider CProvider = null;               

                                switch ( sProviderType )               
                                {               
                                        case "CSharp" :       
                                                CProvider = new Microsoft.CSharp.CSharpCodeProvider();
                                                break;
                                        case "VisualBasic" :       
                                                CProvider = new Microsoft.VisualBasic.VBCodeProvider();
                                                break;
                                        default:       
                                                throw new Exception( "参数错误!" );
                                }               

                                ICodeGenerator Generator = CProvider.CreateGenerator();               
                                Generator.GenerateCodeFromCompileUnit( CompileUnit,SWriter,new CodeGeneratorOptions() );               
                                Console.Write( SWriter.ToString() );               
                                return SWriter;               
                                                       
                        }                       
                }                               
        }                                       

        运行结果如下:

        III:CodeMemberField,CodeMemberMethod,CodeMemberProperty,CodeMemberEvent                                                               
        CodeMemberField,CodeMemberMethod,CodeMemberProperty,CodeMemberEvent是产生成员函数,成员变量,成员事件,                                                               
成员属性的对象,而且必须要由CodeTypeDeclaration.Members对象来加入。                                                                       
        那么我们再次对 CodeDOMTest() 函数进行修改:                                                               
        /*                                                               
         * CodeDOM测试函数:                                                               
         * 演示 <一>:提供了表示许多常见的源代码元素类型的类型。                                                               
         * 演示 <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。               
         */               
        private static CodeCompileUnit CodeDOMTest()               
        {               
                CodeCompileUnit CompileUnit = new CodeCompileUnit();       
                CodeNamespace   CNameSpace = new CodeNamespace("CodeDOMSpace");       
                try       
                {       

                        CNameSpace.Imports.Add(new CodeNamespaceImport("System"));
                        CNameSpace.Imports.Add(new CodeNamespaceImport("System.IO"));

                        CodeTypeDeclaration CClass = new CodeTypeDeclaration( "CodeDOMClass" );
                        CClass.BaseTypes.Add( new CodeTypeReference( typeof(System.IO.File) ) );
                        CNameSpace.Types.Add( CClass );

                        CodeTypeDeclaration CInterFace = new CodeTypeDeclaration( "CodeDOMInterFace" );       
                        CInterFace.IsInterface = true;       
                        CNameSpace.Types.Add( CInterFace );       

                        CodeTypeDeclaration CAbstractClass = new CodeTypeDeclaration( "CodeDOMAbstractClass" );       
                        CAbstractClass.TypeAttributes = System.Reflection.TypeAttributes.Abstract |       
                                System.Reflection.TypeAttributes.Public;
                        CNameSpace.Types.Add( CAbstractClass );       

                        CodeTypeDeclaration CMethodClass = new CodeTypeDeclaration( "CodeDOMMethedClass" );       

                        CodeMemberField CField = new CodeMemberField( typeof(int),"iTemp");       

                        CodeMemberMethod CMethod = new CodeMemberMethod();       
                        CMethod.Name = "TestMethod";       
                        CMethod.ReturnType = new CodeTypeReference( typeof(string) );       
                        CMethod.Parameters.Add(                                               
                                new CodeParameterDeclarationExpression( typeof(int), "InputInt")        );                               
                        CMethod.Statements.Add( new CodeMethodReturnStatement(                                               
                                                        new CodePrimitiveExpression("Hello World!") ));               

                        CodeMemberProperty CMProperty = new CodeMemberProperty();                                               
                        CMProperty.Name = "ITemp";                                               
                        CMProperty.HasGet = true;                                               
                        CMProperty.Type = new CodeTypeReference( typeof(int) );                                               
                        CMProperty.GetStatements.Add(        new CodeMethodReturnStatement(                                       
                                                                        new CodeFieldReferenceExpression(
                                                                        new CodeThisReferenceExpression(), "iTemp")));

                        CMethodClass.Members.Add(CField);                                               
                        CMethodClass.Members.Add(CMethod);                                               
                        CMethodClass.Members.Add(CMProperty);                                               

                        CNameSpace.Types.Add( CMethodClass );

                        CompileUnit.Namespaces.Add( CNameSpace );

                }       
                catch       
                {       
                        throw new Exception("CodeDOM出错!");
                }       

                return CompileUnit;       
        }               

        CodeMemberField,CodeMemberProperty没什么可讲的,大体应用上面都有了,着重说一下CodeMemberMethod吧。               
        首先Create一个表示事件的对象:               
        CodeMemberMethod CMethod = new CodeMemberMethod();
        然后给出事件名字:
        CMethod.Name = "TestMethod";
        然后就是函数的返回值如何给了:

        public CodeTypeReference ReturnType { get; set; }
        而CodeMemberMethod的返回值是CodeTypeReference类型
        所以需要如下给出:
        CMethod.ReturnType = new CodeTypeReference( typeof(string) );

        函数的参数添加如下:
        CMethod.Parameters.Add( new CodeParameterDeclarationExpression( typeof(string), "AString") );


        函数的返回值添加如下:
        CMethod.Statements.Add( new CodeMethodReturnStatement(
                                                        new CodePrimitiveExpression("Hello World!") ));

三:CodeDOM如何编译生成的源代码:                                                       
        在名称空间里再加入一个带有入口函数Main的类:                                               

        CodeTypeDeclaration MainClass = new CodeTypeDeclaration("MainClass");//定义一个名为DemoClass的类                                                
        CodeEntryPointMethod Start = new CodeEntryPointMethod();             //定义程序入口点,就是Main()                                                
        //下面两句产生调用方法的语句                                                
        //这句会产生如下的C#代码 System.Console.WriteLine("Hello World!");                                                
        CodeMethodInvokeExpression CMDInvoke_1 = new CodeMethodInvokeExpression(                                               
                new CodeTypeReferenceExpression("System.Console"),                                        
                WriteLine,new CodePrimitiveExpression("Hello World!"));                                       

        //这句是 System.Console.Read();                                                
        CodeMethodInvokeExpression CMDInvoke_2 = new CodeMethodInvokeExpression(                                               
                new CodeTypeReferenceExpression("System.Console"),                                        
                Read);
        MainClass.Members.Add( Start );        
        Start.Statements.Add( CMDInvoke_1 );        
        Start.Statements.Add( CMDInvoke_2 );        

        CNameSpace.Types.Add( MainClass );       

        然后加一个编译的函数CompileCode,这个就很简单了,见下面代码。       


        为了编译生成的源代码,我们重构了之前的代码,我给出主要的结构函数吧:       
        /*       
        * 编译生成的源代码:       
        * <四>:CodeDOM也可以用于将源代码编译成二进制程序集。       
        */       
        public static CompilerResults CompileCode( CodeDomProvider CProvider, string SourcePath )        
        {                

                ICodeCompiler ICompiler = ((Microsoft.CSharp.CSharpCodeProvider)CProvider).CreateCompiler();        

                //编译参数        
                CompilerParameters CParameters = new CompilerParameters(        
                        new string[] {"System.dll"},
                        SourcePath.Substring( 0,SourcePath.LastIndexOf(".") + 1 ) + "exe",
                        false);

                CParameters.GenerateExecutable = true; //生成exe       

                CompilerResults CResult = ICompiler.CompileAssemblyFromFile( CParameters,SourcePath );        

                return CResult;        
        }                


        /*               
         * 根据CodeCompileUnit生成源代码,并且放到StreamWrite中,并保存到文件:               
         * 演示  <三>:可以使用受支持的编程语言的 CodeDOM 代码生成器,将该对象图呈现为源代码。               
         */               
        private static void GetCodeDOMSource( CodeCompileUnit CompileUnit, String sProviderType )               
        {               
                string SourcePath =  "CodeDOMTest.cs";       
                StreamWriter StreamW = new StreamWriter( SourcePath,false); //New Insert       

                CodeDomProvider CProvider = null;       

                switch ( sProviderType )       
                {       
                        case "CSharp" :
                                CProvider = new Microsoft.CSharp.CSharpCodeProvider();
                                break;
                        case "VisualBasic" :       
                                CProvider = new Microsoft.VisualBasic.VBCodeProvider();
                                break;
                        default:       
                                throw new Exception( "参数错误!" );
                }               

                ICodeGenerator Generator = CProvider.CreateGenerator();               
                try               
                {               
                        Generator.GenerateCodeFromCompileUnit( CompileUnit,StreamW,new CodeGeneratorOptions() ); //New Insert       
                        StreamW.Close();       
                        CompileCode( CProvider, SourcePath );        
                }               
                catch(Exception e)       
                {       
                        throw new Exception(e.Message);
                }               
        }               

        /*               
         * CodeDOM测试函数:               
         * 演示 <一>:提供了表示许多常见的源代码元素类型的类型。               
         * 演示 <二>:可以设计一个生成源代码模型的程序,使用 CodeDOM 元素构成一个对象图。               
         */               
        private static CodeCompileUnit CodeDOMTest()               
        {               
                CodeCompileUnit CompileUnit = new CodeCompileUnit();       
                CodeNamespace   CNameSpace = new CodeNamespace("CodeDOMSpace");       
                try       
                {               

                        CodeTypeDeclaration MainClass = new CodeTypeDeclaration("MainClass");//定义一个名为DemoClass的类        
                        CodeEntryPointMethod Start = new CodeEntryPointMethod();           //定义程序入口点,就是Main()        
                        //下面两句产生调用方法的语句        
                        //这句会产生如下的C#代码 System.Console.WriteLine("Hello World!");        
                        CodeMethodInvokeExpression CMDInvoke_1 = new CodeMethodInvokeExpression(       
                                new CodeTypeReferenceExpression("System.Console"),
                                WriteLine,new CodePrimitiveExpression("Hello World!"));

                        //这句是 System.Console.Read();        
                        CodeMethodInvokeExpression CMDInvoke_2 = new CodeMethodInvokeExpression(       
                                new CodeTypeReferenceExpression("System.Console"),
                                ReadLine);
                        MainClass.Members.Add( Start );        
                        Start.Statements.Add( CMDInvoke_1 );        
                        Start.Statements.Add( CMDInvoke_2 );

                        CNameSpace.Types.Add( MainClass );

                        CompileUnit.Namespaces.Add( CNameSpace );
                }       
                catch       
                {       
                        throw new Exception("CodeDOM出错!");
                }       
                return CompileUnit;       
        }               

        使用:               
        CodeCompileUnit CompileUnit = CodeDOMTest();               
        GetCodeDOMSource( CompileUnit, "CSharp" );               
        GetCodeDOMSource( CompileUnit, "VisualBasic" );       

        编译之后,就会在Debug目录下有两个EXE文件,一个是本程序的,一个是CodeDOM编译源代码生成的。       

        到此CodeDOM的技术就讲完了,根据CodeDOM我们可以写自己的代码解析器,事实上有人已经做了^-^       

        参考资料:       
                MSDN.NET2003
                深入解剖分析ASP.NET组件设计 黄忠诚

TOP

发新话题