CodeDom


細說CodeDom

在上一篇文章中,老周厚着臉皮給大伙介紹了代碼文檔的基本結構,以及一些代碼對象與CodeDom類型的對應關系。

在評論中老周看到有朋友提到了 Emit,那老周就順便提一下。嚴格上說,Emit並不是針對代碼文檔生成和編譯而設計的,Emit一方面可以實時發出 IL 指令,另一方面也支持動態程序集,即可以在運行時創建程序集,並可以定義類型,然后可以執行。而CodeDom所針對的是代碼文檔的生成和編譯,所以說,是有所不同的。

哦,是了,還有一個玩意兒挺有趣,也提一下吧——動態 Linq 表達式樹。它也跟動態編譯有點像,就是動態創建 LINQ表達式樹,LINQ懂吧,別告訴你不知道,這是玩.net的必備法寶,表達式樹創建后會實時編譯為一個委托實例,使用時直接調用生成的委托實例即可。

好,下面開始本文的內容。先說說表達式,因為語句是由表達式組成的,按照正常人類的思考方式,應當由小及大來學習。啥是表達式呢,其實可以說,表達式是代碼文檔的基礎元素,比如一個int值 2500,就是一個表達式;字符串常量用雙引號包起來,如"abc",也是一個表達式;當前類實例的引用 this也是表達式;基類實例的引用 base,也是表達式;變量名 a 也是表達式;數組索引,如 [0] 也是表達式;方法中的輸出參數 out 也是表達式……

CodeExpression 是所有表達式對象的公共基類,從它的派生類來看,咱們不妨對表達式的類型先來個非專業總結,這樣有助於大家掌握思路。這個類的派生類相當多,不要暈,思路理清了,就不怕它數量多。

老周大致把這些表達式類划分以下幾類(僅供參考):

1、創建實例。如CodeArrayCreateExpression、CodeDelegateCreateExpression等,大家可以根據它們的名字來猜猜其作用,現在你不必弄明白到底怎么用,后面老周會教你怎么用的。

2、引用。比如當前實例引用(this)CodeThisReferenceExpression,再比如引用某個實例的方法的語句 CodeMethodReferenceExpression, 像 x.Run(……)。再比如屬性里面set訪問器中,大家都知道有一個 value 關鍵字,要引用它可以用 CodePropertySetValueReferenceExpression 類。

3、運算符(操作符)。這好懂了吧,+-*/=()==!<>這些都是操作符。對於二進制運算,可以用CodeBinaryOperatorExpression,它通過 CodeBinaryOperatorType 枚舉來規范要用到的運算符,如加法、減法等。如果要進行類型轉換,可以使用類型轉換運算符 CodeCastExpression,它也是一種表達式。如果想用 typeof 運算符,可以使用 CodeTypeOfExpression 類。

4、其他。比較零碎。像參數引用,數組索引等。

 

很多表達式類在使用時,直接賦相應的值就行,用習慣了之后也不會很難。在舉例之前,老周先講一下如何生成代碼,相信大伙在做完例子后,最迫切的就是想看看生成的代碼是啥樣子的。

生成代碼要用 CodeDomProvider 類(位於System.CodeDom.Compiler命名空間下),這個類不用 new 的,它有一個靜態的 CreateProvider 方法,調用后直接返回 CodeDomProvider 實例,調用方法時,可以用一個字符來指定編程語言。比如,C#、cs、CSharp都可以表示C#語言;用 VB、VisualBasic都表示VB語言;用js、JScript表示jscript語言;用cpp表示C++語言(托管)。名字是不區分大小寫的,所以,CS和cs都一樣。

然后,你會看到這個類有一堆GenerateCodeFromXXXX的方法,這些XXXX可以是表達式、語句、編譯單元、類型定義等。生成代碼時,你必須提供一個 TextWriter,代碼生成后會寫進這個writer里面。

下面給大家演示一下,咱們就把代碼輸出到控制台吧,這樣馬上就能看到效果。

假設我定義一個類型,名叫 Dog,它是結構。來,看代碼:

            CodeTypeDeclaration dcl = new CodeTypeDeclaration("Dog");
            dcl.IsStruct = true;
            dcl.Attributes = MemberAttributes.Public;

IsStruct 指明它是個結構,CodeTypeDeclaration類構造函數中傳的是類型的名字,叫Dog。Attributes屬性設置這個類型為公共類型(public)。

好,現在我們就定義好一個類型了,下面咱們來生成C#、VB和C++三種語言的代碼。

復制代碼
            // 生成C#代碼
            CodeDomProvider provider = CodeDomProvider.CreateProvider("cs");
            Console.WriteLine("生成 C# 代碼:");
            provider.GenerateCodeFromType(dcl, Console.Out, null);

            // 生成 VB 代碼
            provider = CodeDomProvider.CreateProvider("vb");
            Console.WriteLine("\n生成 VB 代碼:");
            provider.GenerateCodeFromType(dcl, Console.Out, null);

            // 生成 C++ 代碼
            provider = CodeDomProvider.CreateProvider("cpp");
            Console.WriteLine("\n生成 C++ 代碼:");
            provider.GenerateCodeFromType(dcl, Console.Out, null);
復制代碼

運行后,就會看到以下內容。

 

方法很簡單,先用CreateProvider靜態方法獲取特定語言的提供程序,然后 GenerateCodeFromXXXXX,你要從什么代碼對象生成,就調用哪個版本,比如要從編譯單元生成代碼,就應當調用GenerateCodeFromCompileUnit方法,要從定義的命名空間生成代碼就調用GenerateCodeFromNamespace方法。

 

好,咱們開始練習,先來個操作符的,下面例子用來生成 a + b,a和b是變量名,我們先不管變量是哪來的,反正目的是使用操作符。

            CodeVariableReferenceExpression left = new CodeVariableReferenceExpression("a");
            CodeVariableReferenceExpression right = new CodeVariableReferenceExpression("b");
            CodeBinaryOperatorExpression opt = new CodeBinaryOperatorExpression();
            opt.Operator = CodeBinaryOperatorType.Add;
            opt.Left = left;
            opt.Right = right;

一個操作符表達式,通常會三個組成元素——運算符,左邊操作數,右邊操作數。在這個例子里面,左邊和右邊分別使用變量引用,即用到 CodeVariableReferenceExpression 類,這個類用法也簡單,只要提供一個變量名稱就行了。

最后生成代碼為:

(a + b)

 

再看一個例子。

復制代碼
            CodeThisReferenceExpression thisexpr = new CodeThisReferenceExpression();
            CodeFieldReferenceExpression fexp = new CodeFieldReferenceExpression();
            fexp.FieldName = "m_val";
            fexp.TargetObject = thisexpr;

            CodeBinaryOperatorExpression opt = new CodeBinaryOperatorExpression();
            opt.Left = fexp;
            opt.Right = new CodePrimitiveExpression((int)300);
            opt.Operator = CodeBinaryOperatorType.Assign;
復制代碼

上面例子中的 CodeBinaryOperatorExpression 對象指定運算符為 Assign, 即賦值符號(=)。然后我們再看它兩邊的操作數。左邊引用的是當前實例的字段,首先要創建一個CodeThisReferenceExpression,這個類不需要指定任何參數,因為它生成的就是this關鍵字,然后用CodeFieldReferenceExpression來引用this實例中一個叫m_val的字段;右邊操作數是一個常量,常量值可以用CodePrimitiveExpression來表示。

CodePrimitiveExpression 一般用於指定基礎類型的常量值,如int、string、double等。如這樣

CodePrimitiveExpression p = new CodePrimitiveExpression(0.1322d);

生成代碼后,會自動將傳入的值表示為double類型常量。生成代碼如下:

0.1322D

再比如

  CodePrimitiveExpression p = new CodePrimitiveExpression("ask");

就會生成字符串常量:

"ask"

好了,上面的賦值表達式最終得到的結果如下:

(this.m_val = 300)

 

下面咱們來生成一句 typeof表達式。

復制代碼
            CodeTypeOfExpression texp = new CodeTypeOfExpression(typeof(string));

            Console.WriteLine("C# 代碼:");
            CodeDomProvider prd = CodeDomProvider.CreateProvider("cs");
            prd.GenerateCodeFromExpression(texp, Console.Out, null);

            Console.WriteLine("\n");
            Console.WriteLine("VB 代碼:");
            CodeDomProvider prd2 = CodeDomProvider.CreateProvider("vb");
            prd2.GenerateCodeFromExpression(texp, Console.Out, null);
復制代碼

很簡單,實例化 CodeTypeOfExpression 時,把某個類型的type傳進去就行了。

最后輸出的代碼如下:

C# 代碼:
typeof(string)

VB 代碼:
GetType(String)

 

咱們再來個類型轉換的表達式。

            CodeVariableReferenceExpression vexp = new CodeVariableReferenceExpression();
            vexp.VariableName = "x";
            CodeTypeReference tref = new CodeTypeReference(typeof(decimal));
            CodeCastExpression cexp = new CodeCastExpression();
            cexp.Expression = vexp;
            cexp.TargetType = tref;

CodeVariableReferenceExpression主要設置兩個參數,Expression 指的是要進行類型轉換的對象,通常是一個變量;TargetType是要轉換的目標類型,需要用一個CodeTypeReference來封裝,使用時直接把類型的type傳遞即可。

類型轉換表達式生成代碼如下:

((decimal)(x))

 

=================================================

學會使用表達式后,語句就好辦了,因為語句就是由表達式組成的,只是為了說明是語句,在C類風格的語言中會以英文的分號結尾(VB除外)。

來,來一句賦值語句。

            CodeVariableReferenceExpression left = new CodeVariableReferenceExpression("f");
            CodePrimitiveExpression sexp = new CodePrimitiveExpression("so hot");
            CodeAssignStatement assstm = new CodeAssignStatement();
            assstm.Left = left;
            assstm.Right = sexp;

CodeAssignStatement 和剛才的賦值表達式很像,也需要指定左邊的表達式和右邊的表達式。最后生成的代碼如下:

f = "so hot";

大家看到了,語句結尾是有分號的,剛才的表達式是沒有分號的。

 

接下來,咱們聲明一個變量,然后給它一個值。

復制代碼
            CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement(typeof(int), "n");

            CodeAssignStatement ass = new CodeAssignStatement();
            ass.Left = new CodeVariableReferenceExpression("n");
            ass.Right = new CodePrimitiveExpression(98000);

            CodeDomProvider prd = CodeDomProvider.CreateProvider("cs");
            prd.GenerateCodeFromStatement(decl, Console.Out, null);
            prd.GenerateCodeFromStatement(ass, Console.Out, null);
復制代碼

這段實際上是生成了兩句代碼,第一句是聲明語句,CodeVariableDeclarationStatement將產生聲明變量的語句,需要指定變量的類型和變量名。

第二句是賦值語句,需要指定左邊和右邊。左邊引用變量n,右邊是常量值。

生成代碼如下:

int n;
n = 98000;

以上代碼不夠簡潔,我們完全可以在聲明變量的時候,就將它初始化,這樣只用一個語句就可以了。

CodeVariableDeclarationStatement decl = new CodeVariableDeclarationStatement(typeof(int), "n", new CodePrimitiveExpression(98000));

這樣一個語句就完成了,生成的代碼如下:

int n = 98000;

 

這里老周不會將所有語句一個個做介紹。本文介紹的這些表達式和語句,主要是幫助初學者朋友們練手,以便找到感覺,剩下的一些復雜的語句——如選擇語句、循環語句這些,老周在后面的文章中會介紹的。

OK,今天的牛皮就吹到這里,希望對各位有幫助。

 

 
分類:  個人文章
標簽:  CodeDom表達式語句


免責聲明!

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



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