標籤:擷取 field setvalue 自動 arp unit 看到了 .com binary
在上一篇文章中,老周厚著臉皮給大夥介紹了代碼文檔的基本結構,以及一些代碼對象與CodeDom類型的對應關係。
在評論中老周看到有朋友提到了 Emit,那老周就順便提一下。嚴格上說,Emit並不是針對代碼文檔產生和編譯而設計的,Emit一方面可以即時發出 IL 指令,另一方面也支援動態程式集,即可以在運行時建立程式集,並可以定義類型,然後可以執行。而CodeDom所針對的是代碼文檔的產生和編譯,所以說,是有所不同的。
哦,是了,還有一個玩意兒挺有趣,也提一下吧——動態 Linq 運算式樹狀架構。它也跟動態編譯有點像,就是動態建立 LINQ運算式樹狀架構,LINQ懂吧,別告訴你不知道,這是玩.net的必備法寶,運算式樹狀架構建立後會Just-In-Time 編譯為一個委託執行個體,使用時直接調用產生的委託執行個體即可。
好,下面開始本文的內容。先說說運算式,因為語句是由運算式組成的,按照正常人類的思考方式,應當由小及大來學習。啥是運算式呢,其實可以說,運算式是代碼文檔的基礎元素,比如一個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,今天的牛皮就吹到這裡,希望對各位有協助。
【.net 深呼吸】細說CodeDom(2):運算式、語句