元件
元件技術是軟體領域一項非常大的成就。元件技術像搭積木遊戲一樣,在搭積木時,我們知道並不是任何的兩個積木都可以組合,能組合的積木之間必定存在可以銜接的介面,同時,已經搭好的積木可以作為一個整體的大積木,被用來與其他積木組合。
在軟體領域,這些“積木”模型有微軟的COM,Sun的JavaBean、.Net的程式集等。
元件可以提高子程式的重用性和靈活性,並便於子程式的釋出。也可以節省開發時間,提高程式的可靠性。
例如,Shapes類包含代表圓、三角形或其他圖形的物件。它的方法可用於計算圖形面積或執行圖形的其他操作。許多程式都要使用Shape類,完成如下功能:加色/繪圖程式、Visio/PPT/Word畫圖、建築/施工設計、CAD、遊戲及其他程式。
如果把Shapes設計成元件,僅定義一次,被在多個程式重用性,豈不美哉?(注意:不是在多個程式中Ctrl+C、Ctrl+V該類,這就太程式碼工人了。)
在.Net程式設計模型中,我們使用程式集來實現元件技術的使用。
程式集
對於.Net來講,無論C#、VB、J#那種語言的應用程式編譯的最終結果都是一個程式集。編譯而成的程式集包括我們提到的Exe檔案或DLL檔案。.Net編譯生成的Exe檔案和常規我們見的二進位制可執行檔案(exe檔案,比如說QQ.exe)是不一樣的,它是由中間語言(IL)組成的可執行檔案,必須由CLR來執行。
程式集具有以下特點:
· 程式集作為 .exe 或 .dll 檔案實現。
· 通過將程式集放在全域性程式集快取中,可在多個應用程式之間共享程式集。
· 要將程式集包含在全域性程式集快取中,必須對程式集進行強命名。
· 程式集僅在需要時才載入到記憶體中。
· 可以使用反射以程式設計方式獲取關於程式集的資訊。
建立與使用程式集
第1步:建立控制檯專案,給專案改名為“CalcShapes”,如下圖:
第3步:檢視解決方案,並修改Shapes類庫專案中的“Class1.cs”名為“Circle.cs”
第4步:編寫Circle類的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
usingSystem; usingSystem.Collections.Generic; usingSystem.Text; namespace Shapes { public classCircle { double radius; public doubleRadius { get{ return radius; } set{ radius = value; } } public Circle() { radius = 0; } public Circle(doublegivenRadius) { radius = givenRadius; } public doublegetArea() { returnSystem.Math.PI * (radius * radius); } } } |
第5步:在控制檯程式使用Shapes專案中的程式集,在CalcShapes專案的“引用”上右鍵單擊“新增引用”,然後彈出下面的對話方塊,選擇“專案”選項卡,選擇“Shapes”專案,然後點選“確定”按鈕即可。
第6步:在控制檯程式中編寫程式碼,並檢視執行效果;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
usingSystem; usingSystem.Collections.Generic; usingSystem.Text; usingShapes; //1.引用名稱空間; namespaceCalcShapes { class Program { static voidMain(string[]args) { Circlecircle1 = new Circle(); circle1.Radius = 3.3; //設定圓周率 Console.WriteLine(circle1.getArea().ToString()); //現實圓面積 } } } |
專案1:使用Winform來新增Shapes類引用
專案背景:針對我們已經寫好的程式集檔案,方便我們在以後在各種程式中使用,所以,我們應該從各個方面將該元件淋漓盡致使用的“壓榨乾淨”。在上述基礎上,新增新專案
解決方案:學習新增Winform專案
步驟1:新增Winform新專案到剛才的專案,如下圖:
步驟4:編寫程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
usingSystem; usingSystem.Collections.Generic; usingSystem.ComponentModel; usingSystem.Data; usingSystem.Drawing; usingSystem.Text; usingSystem.Windows.Forms; usingShapes; //1.引用名稱空間 namespaceWinCalc { public partialclass Form1 : Form { public Form1() { InitializeComponent(); } private voidbutton1_Click(object sender, EventArgs e) { doubleradious = 0.0; Circlecircle1 = new Circle(); circle1.Radius = Convert.ToDouble(radious); MessageBox.Show("圓的面積為:" +circle1.getArea().ToString()); } } } |
步驟5:設定WinCalc為啟動專案,如下圖,然後執行(F5):
步驟6:出現執行介面,輸入半徑,如下圖:
思考與擴充,在.Net中程式集元件引用的本質是多加幾個類庫專案,而是引用的其中的核心元件是什麼?
請注意新增引用的本質是類庫專案所對應的程式集檔案,如Shapes\bin\DebugShapes.dll檔案。
【擴充在VB下使用C#的dll檔案】
1. 介面設計省略同C#
2.代買比較痛苦: PublicClass Form1
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Private Sub Button1_Click(ByVal sender AsSystem.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim circle As Shapes.Circle circle = New Shapes.Circle() circle.Radius = 3.3 MessageBox.Show("VB圓面積" +circle.getArea().ToString()) End Sub End Class |
2. 效果如下:
深入研究程式集
對於可執行的程式集來講,包含五部分的資訊:
n PE32頭
這是標準的Windows可執行檔案頭,它可以引導作業系統對此檔案進行執行。二進位制的可執行檔案也必須有這個頭作業系統才能執行。PE頭裡包含了標識檔案型別的資訊:Exe或Dll;除此之外裡面還包括其生成時間以及入口方法地址(Main())等。作業系統可以通過讀取這個頭將託管模組載入到記憶體中然後呼叫CLR從入口方法開始執行。
n 後設資料
後設資料是託管模組中最重要的資料。後設資料用來表述託管模組中包含的型別資訊,這些資訊包括類、介面、方法、欄位、屬性、事件等程式碼元素的定義資訊。每個程式集都包含有後設資料表,後設資料表通常有兩種,一種是表述程式碼中的型別或成員的列表;另外一種是表述原始碼中引用的型別和成員的表。在模組或程式集中定義和引用的每個型別和成員都將在後設資料中進行說明。當執行程式時,執行庫將載入後設資料,並可以通過它來發現有關程式碼的類、成員、繼承等資訊。
n 中間語言
.Net編譯器將程式語言的原始碼編譯後生成的中間語言程式碼,在執行時,CLR將這些中間語言程式碼編譯成CLR執行。
n 資源
資源是指程式在執行時所需要的資源,包括圖片、字串等。在預設情況下,一個程式集可以包含資源也可以不包含資源。如果程式集包含資源,那麼程式集中會有一個叫做資源清單的結構專門來描述和連線這些資原始檔,公共語言執行庫在執行時會管理或載入這些資原始檔。
n 程式集清單
程式集清單包含描述該程式集中各元素彼此如何關聯的資料集合。這些資料包括:程式集名稱、版本號、區域性、程式集中檔案所有檔案的列表、型別引用資訊、有關被引用程式集的資訊等。其中微軟規定的前四項(程式集名稱、版本號、區域性和強名稱資訊)構成了程式集的標識。檢視.Net建立的任何專案擬都會發現一個叫“AssemblyInfo”的檔案,這個檔案就是用來設定程式集清單的,一般在建立檔案時會被自動生成。
反射及其使用
提到反射,首先我們會想到中學物理,光的反射是成像的基礎。如果沒有光的反射,你將看不到任何東西。如果沒有反射,你也不能照鏡子,就算你再帥再靚,你也不知道。
對於物體來說,你可以直接檢視該物體,也可以通過鏡子來檢視該物體。這些概念和軟體技術中反射的概念很相似。在軟體技術中,反射代表一種方法,一種通過間接途徑來檢視和訪問程式集中元素的方法。
反射本質
C#專案在編譯後生成程式集,而程式集通過記錄了程式碼中的所有型別。假如說,我們目前有某個元件的程式集,而沒有原始碼,我們能不能通過一種機制從程式集中取出各種型別進行操作呢?答案是肯定的,在.Net中可以通過反射機制從程式中提取任何存在的型別,包括類、結構、委託、介面和列舉等,提取出型別後,利用反射機制我們甚至可以重新生成這些型別的物件,然後使用這些型別的成員,包括方法、屬性、事件、構造方法等。在一般情況下,我們可以使用反射來執行以下任務:
n 獲取程式集中有關公共型別及其公共成員的資訊
n 瞭解程式碼所在的模組和程式集
n 呼叫程式集中型別物件公共屬性或方法
要使用反射操作某個程式集中的型別,基本分為兩步走:
1. 載入要反射的程式集;
2. 然後通過Type類的方法對程式集中的型別進行列舉或呼叫。
因為對非公共資訊的訪問將帶來安全風險,所以.Net反射機制在預設情況下只能對程式集中的公共型別的資訊或成員進行反射。
使用反射
在使用反射之前,我們首先需要載入程式集。需要使用System.Reflection名稱空間下的Assembly類的Load方法或LoadFrom方法。這兩個方法都是靜態方法:
Assembly.Load(String 程式集名稱)
例如:Assembly assA=Assembly.Load(“Shapes”);
Assembly.LoadFrom(String 程式集路徑)
例如:Assembly assB=Assembly.LoadFrom(@”F:\MyShapes\bin\Debug\MyShaps.dll”)
接下來,通過Assembly的GetType和GetTypes方法來獲得載入程式集的型別了。前者只能獲得程式集裡一個型別,後者可以獲得程式集裡所有型別,原型如下:
1 2 3 |
public Type GetType(string name) //根據類名獲得該型別 public Type[] GetTypes() //獲得程式集的所有型別 |
一旦獲得程式集中的型別,就可以重建型別對應的物件。有關Type類的常用方法如下:
Type方法 |
返回型別 |
操作說明 |
GetMethod/GetMethods | MethodInfo | 使用MethodInfo物件封裝的方法名稱、返回型別、引數、訪問修飾符等 |
GetField/GetFields | FieldInfo | 使用FieldInfo封裝型別的欄位名稱、訪問修飾符等 |
GetEvent/GetEvents | EventInfo | 使用EventInfo封裝型別的欄位名稱、事件處理程式、資料型別、自定義屬性、宣告型別和反射型別等 |
GetProperty/GetProperties | PropertyInfo | 使用PropertyInfo封裝屬性的名稱、資料型別、宣告型別等 |
GetConstructor/GetConstructors | ConstructorInfo | 使用ConstrucorInfo封裝構造方法的名稱、引數、訪問修飾符等資訊 |
小結:
在本章中,我們主要學習了:
u 元件的歷史和應用
u .Net中的元件技術
u 反射技術的使用
u 序列化
英語詞彙:
英文 中文
Attribute 屬性
Positional 狀態的
Element 元素
Description 描述
Temperature 溫度
Fahrenheit 華氏溫度
Service 服務
Import 匯入
Inherited 繼承的
All 所有的
Assembly 裝配、集合、元件
Module 模組
Struct 結構
Specify 指定
練習專案:
1.試將以前我們寫的計算器的計算類生成.dll程式集,供其他地方使用。