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有着無限的潛能,
唯一的約束恐怕在於人們的想象力。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM