[原创] 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组件设计 黄忠诚