C# 自动生成类代码文件

/*注意,先导入下面的命名空间

using System.CodeDom

using System.CodeDom.Compiler;

using Microsoft.CSharp;

using System.Reflection;

*/

//准备一个代码编译器单元

CodeCompileUnit unit = new CodeCompileUnit();

//准备必要的命名空间(这个是指要生成的类的空间)

CodeNamespace sampleNamespace=new CodeNamespace("Xizhang.com");

//导入必要的命名空间

sampleNamespace.Imports.Add(new CodeNamespaceImport("System"));

//准备要生成的类的定义

CodeTypeDeclaration Customerclass = new CodeTypeDeclaration("Customer");

//指定这是一个Class

Customerclass.IsClass = true;

Customerclass.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed;

//把这个类放在这个命名空间下

sampleNamespace.Types.Add(Customerclass);

//把该命名空间加入到编译器单元的命名空间集合中

unit.Namespaces.Add(sampleNamespace);

//这是输出文件

string outputFile = "Customer.cs";

//添加字段

CodeMemberField field = new CodeMemberField(typeof(System.String), "_Id");

field.Attributes = MemberAttributes.Private;

Customerclass.Members.Add(field);

//添加属性

CodeMemberProperty property = new CodeMemberProperty();

property.Attributes = MemberAttributes.Public | MemberAttributes.Final;

property.Name = "Id";

property.HasGet = true;

property.HasSet = true;

property.Type = new CodeTypeReference(typeof(System.String));

property.Comments.Add(new CodeCommentStatement("这是Id属性"));

property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_Id")));

property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_Id"), new CodePropertySetValueReferenceExpression()));

Customerclass.Members.Add(property);

//添加方法(使用CodeMemberMethod)--此处略

//添加构造器(使用CodeConstructor) --此处略

//添加程序入口点(使用CodeEntryPointMethod) --此处略

//添加事件(使用CodeMemberEvent) --此处略

//添加特征(使用 CodeAttributeDeclaration)

Customerclass.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(SerializableAttribute))));

//生成代码

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

CodeGeneratorOptions options

= new CodeGeneratorOptions();

options.BracingStyle = "C";

options.BlankLinesBetweenMembers = true;

using (System.IO.StreamWriter sw = new System.IO.StreamWriter(outputFile))

{

provider.GenerateCodeFromCompileUnit(unit, sw, options);

}

参考文件:http://www.cnblogs.com/zgx/archive/2010/12/29/1920347.html

.NET推崇这样一种思想:相对于框架而言,语言处于从属、次要的地位。CodeDom名称空间中包含的类是这一思想的集中体现。我们可以用CodeDom构造一个树或图,用System.CodeDom名称空间的类填充它,完成后,
用对应各种.NET语言的CodeProvider对象将树结构转换成该种语言的代码。要更换一种语言,简单到只需更换一下最后用到的CodeProvider对象。   设想一下,利用这一技术,我们至少能够:   ·查询存储过程的元数据,构造出一个负责参数绑定的类。   ·查询程序集的manifest,构造出一个对每个函数执行单元测试的类。   ·为开发组用到的每一种语言生成样板代码。   ·只需写一份范例代码,就可以让用户自由选择他们要查看哪一种语言的版本。   ·自定义模板语法,经解析后生成任意语言的代码。   ·如果要学习某种不熟悉的语言,可以生成该语言的代码,然后将它与熟悉的语言比较。   一、基本操作   System.CodeDom名称空间包含了许多以语言中立的形式描述常见程序结构的对象,每一种语言的细节则由与该种语言对应的CodeProvider对象负责处理。例如,CodeConditionStatement包含一个TrueStatements集合、
一个FalseStatements集合和一个条件属性(Condition attribute),但不涉及条件语句块要用“end if”还是右花括号“}”结束,这部分细节由CodeProvider处理。有了这一层抽象,我们就可以描述待生成的代码结构,
然后将它以任意语言的形式输出,却不必斤斤计较于各种与特定语言有关的细节问题。同时,这种抽象也为我们通过程序改变代码的结构带来了方便。例如,当我们发现某个方法需要增加一个参数时,
就可以将参数加入到该方法的Parameters集合,根本无须改动已生成的代码逻辑。   我们在本文中要用到的大部分对象来自System.CodeDom名称空间,其余对象主要来自各个与特定语言有关的名称空间,例如Microsoft.CSharp名称空间、Microsoft.VisualBasic名称空间、
Microsoft.JScript名称空间和Microsoft.VJSharp名称空间。所有这些面向特定语言的名称空间都包含各自的CodeProvider对象。最后,System.CodeDom.Complier名称空间定义ICodeGenerator接口,
后者用来把生成的代码输出到一个TextWriter对象。   如果我们只要生成一些用于插件或宏的代码片断,可以利用CodeGenerator从Statement、Expression、Type等生成代码。反之,如果我们要生成的是一个完整的文件,则必须从CodeNameSpace对象入手。
在本文的例子中,我们将从一个名称空间开始,示范如何加入import语句、声明类、声明方法、声明变量、实现一个循环结构、索引一个数组,最后,我们将这些技术结合起来,得到一个大家都熟悉的程序。   1.1 初始化名称空间   初始化名称空间的代码类似下面这种形式: private CodeNameSpace InitializeNameSpace(string Name) { // 初始化CodeNameSpace变量,指定名称空间的名称 CodeNameSpace CurrentNameSpace = new CodeNamespace (Name); // 将一些名称空间加入到要导入的名称空间集合。 // 各种语言如何导入名称空间的细节由每种语言对应 // 的CodeProvider分别处理。 CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System")); CurrentNameSpace.Imports.Add (new CodeNamespaceImport("System.Text")); return CurrentNameSpace; }   这段代码定义了一个新的名称空间,并导入System和System.Text名称空间。   1.2 创建类   声明一个新类的代码类似下面这种形式: private CodeTypeDeclaration CreateClass (string Name) { // 新建一个CodeTypeDeclaration对象,指定要创建的类的名称 CodeTypeDeclaration ctd = new CodeTypeDeclaration (Name); // 指定这个CodeType是一个类,而不是一个枚举变量或struct ctd.IsClass = true; // 这个类的访问类型是public ctd.Attributes = MemberAttributes.Public; // 返回新创建的类 return ctd; }   CreateClass函数新建一个指定名称的类,做好为该类植入方法、属性、事件的准备。   1.3 创建方法   声明一个新函数的代码类似下面这种形式: private CodeEntryPointMethod CreateMethod() { // 创建一个方法 CodeEntryPointMethod method = new CodeEntryPointMethod(); // 指定该方法的修饰符:public,static method.Attributes = MemberAttributes.Public | MemberAttributes.Static; // 返回新创建的方法 return method; }   本例创建了一个CodeEntryPointMethod对象。CodeEntryPointMethod对象类似于CodeMemberMethod对象,两者的不同之处在于,CodeProvider会将CodeEntryPointMethod代表的方法作为类的入口点调用,
例如作为Sub Main或void main等。对于CodeEntryPointMethod对象,方法的名称默认为Main;对于CodeMemberMethod,方法的名称必须显式指定。   1.4 声明变量   声明一个变量的代码类似下面这种形式: private CodeVariableDeclarationStatement DeclareVariables(System.Type DataType, string Name) { // 为将要创建的变量类型创建一个CodeTypeReference对象, // 这使得我们不必去关注该类数据在特定语言环境中的 // 与数据类型有关的细节问题。 CodeTypeReference tr = new CodeTypeReference (DataType ); // CodeVariableDeclarationStatement对象使得我们不必纠缠于 // 与特定语言有关的下列细节:在该语言的变量声明语句中, // 应该是数据类型在前,还是变量名称在前;声明变量时是 // 否要用到Dim之类的关键词. CodeVariableDeclarationStatement Declaration = new CodeVariableDeclarationStatement(tr, Name); // CodeObjectCreateExpression负责处理所有调用构造器的细节。 // 大多数情况下应该是new,但有时要使用New。但不管怎样, // 我们不必去关注这些由语言类型决定的细节. CodeObjectCreateExpression newStatement = new CodeObjectCreateExpression (); // 指定我们要调用其构造器的对象. newStatement.CreateType = tr; // 变量将通过调用其构造器的方式初始化. Declaration.InitExpression = newStatement; return Declaration; }   每一种.NET语言都有其特定的数据类型名称,所有这些数据类型都被映射到公共的.NET语言类型。例如,对于C#中称为int的数据类型,在VB.NET中是Integer,公共的.NET类型是System.Int32。
CodeTypeReference对象直接使用.NET公共数据类型,以后由每种语言的CodeProvider将它转换成符合各自语言规范的类型名称。   1.5 初始化数组   初始化一个数组的代码类似下面这种形式: private void InitializeArray (string Name, params char[] Characters ) { // 从参数中传入的字符数组获得一个CodeTypeReference 对象, // 以便在生成的代码中复制该数据类型. CodeTypeReference tr = new CodeTypeReference (Characters.GetType()); // 声明一个匹配原始数组的数组 CodeVariableDeclarationStatement Declaration = new CodeVariableDeclarationStatement (tr, Name); // CodePrimitiveExpression代表“基本”或值数据类型, // 例如char、int、double等等。 // 我们将用这类基本数据类型构成的一个数组来 // 初始化我们正在声明的数组。 CodePrimitiveExpression[] cpe = new CodePrimitiveExpression[Characters.Length]; // 循环遍历原始字符数组, // 为CodePrimitiveExpression类型的数组创建对象。 for (int i = 0; i < Name.Length ; i++) { // 每一个CodePrimitiveExpression将有一个字符的语言 // 中立的表示。 cpe[i] = new CodePrimitiveExpression (Characters[i]); } // CodeArrayCreateExpression负责调用数组中数据类型的 // 默认构造器。 // 由于我们还传入了一个CodePrimitiveExpression的数组, // 所以不必指定数组的大小,且数组中的每一个元素都将有 // 合适的初值。 CodeArrayCreateExpression array = new CodeArrayCreateExpression(tr, cpe); // 指定:该CodeArrayCreateExpression将初始化数组变量声明。 Declaration.InitExpression = array; return Declaration; }   1.6 定义循环结构   声明一个循环结构的代码类似下面这种形式: private CodeIterationStatement CreateLoop(string LoopControlVariableName) { // 声明一个新的变量,该变量将作为 // 循环控制变量 CodeVariableDeclarationStatement Declaration; // 声明一个管理所有循环逻辑的CodeIterationStatement CodeIterationStatement forloop = new CodeIterationStatement(); // 为动态声明的变量指定数据类型的另一种方法: // 用typeof函数获得该数据类型的Type对象,不必 // 用到该类数据的变量 Declaration = new CodeVariableDeclarationStatement(typeof (int), LoopControlVariableName); // 指定一个简单的初始化表达式: // 将新变量设置为0 Declaration.InitExpression = new CodeSnippetExpression ("0"); // 这个新声明的变量将用来初始化循环 forloop.InitStatement = Declaration; // CodeAssignStatement用来处理赋值语句。 // 这里使用的构造器要求提供两个表达式,第一个位于 // 赋值语句的左边,第二个位于赋值语句的右边。 // 另一种办法是:调用默认的构造器,然后分别显式设置 // 左、右两个表达式。 CodeAssignStatement assignment = new CodeAssignStatement( new CodeVariableReferenceExpression(LoopControlVariableName), new CodeSnippetExpression (LoopControlVariableName + " + 1" )); // 在循环迭代中使用赋值语句。 forloop.IncrementStatement = assignment; // 当循环控制变量超出数组中的字符个数时, // 循环结束 forloop.TestExpression = new CodeSnippetExpression (LoopControlVariableName + " < Characters.Length"); return forloop; }   注意,这里我们用typeof函数直接获得循环控制变量的数据类型的Type对象,而不是通过声明一个CodeTypeReference对象的方式。这是CodeVariableDeclartionStatement的又一个构造器,实际上其构造器的总数多达7种。   1.7 索引数组   索引一个数组的代码类似下面这种形式: private CodeArrayIndexerExpression CreateArrayIndex(string ArrayName, string IndexValue ) { // 新建一个CodeArrayIndexerExpression CodeArrayIndexerExpression index = new CodeArrayIndexerExpression (); // Indices属性是一个能够支持多维数组的集合。不过这里我们只需要 // 一个简单的一维数组。 index.Indices.Add ( new CodeVariableReferenceExpression (IndexValue)); // TargetObject指定了要索引的数组的名称。 index.TargetObject = new CodeSnippetExpression (ArrayName); return index; }   CodeArrayIndexerExpression对象处理数组索引方式的种种差异。例如,在C#中数组以ArrayName[IndexValue]的方式索引;但在VB.NET中,数组以ArrayName(IndexValue)的方式索引。   CodeArrayIndexerExpression允许我们忽略这种差异,将注意力集中到其他更重要的问题,例如要索引哪一个数组、要访问第几个数组元素。   二、装配出树结构   我们可以把前面定义的所有函数加入到一个类,通过构造器初始化,例如: public CodeDomProvider() { CurrentNameSpace = InitializeNameSpace("TestSpace"); CodeTypeDeclaration ctd = CreateClass ("HelloWorld"); // 把类加入到名称空间 CurrentNameSpace.Types.Add (ctd); CodeEntryPointMethod mtd = CreateMethod(); // 把方法加入到类 ctd.Members.Add (mtd); CodeVariableDeclarationStatement VariableDeclaration = DeclareVariables (typeof (StringBuilder), "sbMessage"); // 把变量声明加入到方法 mtd.Statements.Add (VariableDeclaration); CodeVariableDeclarationStatement array = InitializeArray ("Characters", 'H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D'); // 把数组加入到方法 mtd.Statements.Add (array); CodeIterationStatement loop = CreateLoop("intCharacterIndex"); // 把循环加入到方法 mtd.Statements.Add (loop); // 数组索引 CodeArrayIndexerExpression index = CreateArrayIndex("Characters", "intCharacterIndex"); // 加入一个语句,它将调用sbMessage对象的“Append”方法 loop.Statements.Add (new CodeMethodInvokeExpression ( new CodeSnippetExpression ("sbMessage"),"Append", index)); // 循环结束后,输出所有字符追加到sbMessage对象 // 后得到的结果 mtd.Statements.Add (new CodeSnippetExpression ("Console.WriteLine (sbMessage.ToString())")); }   构造器的运行结果是一个完整的CodeDom树结构。可以看到,至此为止我们的所有操作都独立于目标语言。最后生成的代码将以属性的形式导出。   三、输出生成结果   构造好CodeDom树之后,我们就可以较为方便地将代码以任意.NET语言的形式输出。每一种.NET语言都有相应的CodeProvider对象,CodeProvider对象的CreateGenerator方法能够返回一个实现了
ICodeGenerator接口的对象。ICodeGenerator接口定义了用来生成代码的所有方法,而且允许我们定义一个用来简化属性输出的辅助方法。下面的辅助方法GenerateCode负责设置好合适的TextWriter以供输出代码,
以字符串的形式返回结果文本。 private string GenerateCode (ICodeGenerator CodeGenerator) { // CodeGeneratorOptions允许我们指定各种供代码生成器 // 使用的格式化选项 CodeGeneratorOptions cop = new CodeGeneratorOptions(); // 指定格式:花括号的位置 cop.BracingStyle = "C"; // 指定格式:代码块的缩进方式 cop.IndentString = " "; // GenerateCodeFromNamespace要求传入一个TextWriter以 // 容纳即将生成的代码。这个TextWriter可以是一个StreamWriter、 // 一个StringWriter或一个IndentedTextWriter。 // StreamWriter可用来将代码输出到文件。 // StringWriter可绑定到StringBuilder,后者可作为一个变量引用。 // 在这里,我们把一个StringWriter绑定到StringBuilder sbCode。 StringBuilder sbCode = new StringBuilder(); StringWriter sw = new StringWriter(sbCode); // 生成代码! CodeGenerator.GenerateCodeFromNamespace(CurrentNameSpace, sw,cop); return sbCode.ToString(); }   有了这个辅助函数,要获取各种语言的代码就相当简单了: public string VBCode { get { VBCodeProvider provider = new VBCodeProvider (); ICodeGenerator codeGen = provider.CreateGenerator (); return GenerateCode (codeGen); } } public string JScriptCode { get { JScriptCodeProvider provider = new JScriptCodeProvider (); ICodeGenerator codeGen = provider.CreateGenerator (); return GenerateCode(codeGen); } } public string JSharpCode { get { VJSharpCodeProvider provider = new VJSharpCodeProvider (); ICodeGenerator codeGen = provider.CreateGenerator (); return GenerateCode (codeGen); } } public string CSharpCode { get { CSharpCodeProvider provider = new CSharpCodeProvider(); ICodeGenerator codeGen = provider.CreateGenerator (); return GeneratorCode (codeGen); } }   四、显示出生成的代码   为输出代码,我们要用到一个简单的.aspx文件,它有四个标签,分别对应一种.NET语言: <table width="800" border="1"> <tr> <th>VB.NET代码</th> </tr> <tr > <td> <asp:Label ID="vbCode" Runat="server" CssClass="code"> </asp:Label> </td> </tr> <tr> <th> C#代码</th></tr> <tr> <td><asp:Label ID="csharpcode" Runat="server" CssClass="code"> </asp:Label></td> </tr> <tr> <th>J#代码</th></tr> <tr > <td> <asp:Label ID="JSharpCode" Runat="server" CssClass="code"> </asp:Label> </td> </tr> <tr> <th>JScript.NET代码</th> </tr> <tr> <td><asp:Label ID="JScriptCode" Runat="server" CssClass="code"> </asp:Label></td> </tr> </table>   在后台执行的代码中,我们实例化一个前面创建的CodeDomProvider类的实例,把它生成的代码赋值给.aspx页面的相应标签的Text属性。为了使Web页面中显示的代码整齐美观,有必要做一些简单的格式化,
替换换行符号、空格等,如下所示: private string FormatCode (string CodeToFormat) { string FormattedCode = Regex.Replace (CodeToFormat, "\n", "<br>"); FormattedCode = Regex.Replace (FormattedCode, " " , " "); FormattedCode = Regex.Replace (FormattedCode, ",", ", "); return FormattedCode; }   下面把生成的代码显示到Web页面: private void Page_Load(object sender, System.EventArgs e) { HelloWorld.CodeDomProvider codegen = new HelloWorld.CodeDomProvider (); vbCode.Text = FormatCode (codegen.VBCode); csharpcode.Text = FormatCode (codegen.CSharpCode); JScriptCode.Text = FormatCode (codegen.JScriptCode); JSharpCode.Text = FormatCode (codegen.JSharpCode); Page.EnableViewState = false; }   输出结果如下: VB.NET代码 Imports System Imports System.Text Namespace HelloWorld Public Class Hello_World Public Shared Sub Main() Dim sbMessage As System.Text.StringBuilder = _ New System.Text.StringBuilder Dim Characters() As Char = New Char() {_ Microsoft.VisualBasic.ChrW(72), _ Microsoft.VisualBasic.ChrW(69), _ Microsoft.VisualBasic.ChrW(76), _ Microsoft.VisualBasic.ChrW(76), _ Microsoft.VisualBasic.ChrW(79), _ Microsoft.VisualBasic.ChrW(32), _ Microsoft.VisualBasic.ChrW(87), _ Microsoft.VisualBasic.ChrW(79), _ Microsoft.VisualBasic.ChrW(82), _ Microsoft.VisualBasic.ChrW(76), _ Microsoft.VisualBasic.ChrW(68)} Dim intCharacterIndex As Integer = 0 Do While intCharacterIndex < Characters.Length sbMessage.Append(Characters(intCharacterIndex)) intCharacterIndex = intCharacterIndex + 1 Loop Console.WriteLine (sbMessage.ToString()) End Sub End Class End Namespace C#代码 namespace HelloWorld { using System; using System.Text; public class Hello_World { public static void Main() { System.Text.StringBuilder sbMessage = new System.Text.StringBuilder(); char[] Characters = new char[] { 'H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D'}; for (int intCharacterIndex = 0; intCharacterIndex < Characters.Length; intCharacterIndex = intCharacterIndex + 1) { sbMessage.Append(Characters[intCharacterIndex]); } Console.WriteLine (sbMessage.ToString()); } } } J#代码 package HelloWorld; import System.*; import System.Text.*; public class Hello_World { public static void main(String[] args) { System.Text.StringBuilder sbMessage = new System.Text.StringBuilder(); char[] Characters = new char[] { 'H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D'} ; for (int intCharacterIndex = 0; intCharacterIndex < Characters.Length; intCharacterIndex = intCharacterIndex + 1) { sbMessage.Append(Characters[intCharacterIndex]); } Console.WriteLine (sbMessage.ToString()); } } JScript.NET代码 //@cc_on //@set @debug(off) import System; import System.Text; package HelloWorld { public class Hello_World { public static function Main() { var sbMessage : System.Text.StringBuilder = new System.Text.StringBuilder(); var Characters : char[] = ['H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D']; for (var intCharacterIndex : int = 0; ; intCharacterIndex < Characters.Length; intCharacterIndex = intCharacterIndex + 1) { sbMessage.Append(Characters[intCharacterIndex]); } Console.WriteLine (sbMessage.ToString()); } } } HelloWorld.Hello_World.Main();   总结:CodeDom体现了.NET中语言的重要性不如框架的思想。本文示范了如何运用一些常用的CodeDom类,几乎每一个使用CodeDom技术的应用都要用到这些类。作为一种结构化的代码生成技术,CodeDom有着无限的潜能,
唯一的约束恐怕在于人们的想象力。

  

posted @ 2017-02-18 21:03  xszjk  阅读(3542)  评论(0编辑  收藏  举报