在.net框架提出之前,編寫(xiě)組件被視為是一種需要高深技巧的工作,令很多人望而生畏,。而.net的出現(xiàn),使得組件的編寫(xiě)變得如此平易近人,,而.net framework的核心語(yǔ)言C#,,更是被稱(chēng)為面向組件的語(yǔ)言。在這里,,我將向大家介紹如何使用C#編寫(xiě)在.net framework環(huán)境下運(yùn)行的組件,,包括如何編寫(xiě)組件類(lèi),如何添加域,、屬性以及事件,,如何編譯和分發(fā)組件。 首先看下面這段足夠簡(jiǎn)單的代碼實(shí)例(在后面我們將慢慢將它變成一個(gè)五臟俱全的組件): using System; namespace ComponentCS { public class StringComponent { private string[] StringsSet; public int StringLength { get { return StringsSet.Length; } } public void Modify(int index,string value) { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new IndexOutOfRangeException(); } else { StringsSet[index]=value; OnModify(); } } public StringComponent() { StringsSet = new string[] { "C# String 0", "C# String 1", "C# String 2", "C# String 3" }; } public string GetString(int index) { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new IndexOutOfRangeException(); } return StringsSet[index]; } } } 一般地,,我們首先創(chuàng)建一個(gè)命名空間(namespace)用來(lái)封裝這個(gè)組件中一系列的類(lèi): namespace CompCS 命名空間的使用非常靈活,,它可以被嵌套,也可以將其內(nèi)部的類(lèi)分別寫(xiě)在多個(gè)文件中,,相應(yīng)地,,還可以在一個(gè)源文件中聲明多個(gè)非嵌套的命名空間。下面是一個(gè)使用嵌套的命名空間的示例代碼: namespace NestIt{ namespace NestedNameSpace { class myClass { public static void DoSth() { ... } } }} 你可以這樣引用類(lèi)myClass: NestIt.NestedNameSpace.myClass.DoSth(); 還是回到我們的命名空間CompCS,,我們使用下面的語(yǔ)句聲明了一個(gè)類(lèi)StringComponent: public class StringComponent 命名空間中的類(lèi)是必需的,,因?yàn)镃#所有的代碼都必須封裝在一個(gè)個(gè)類(lèi)中,所以沒(méi)有類(lèi)的命名空間沒(méi)有任何價(jià)值,。 下面我們?yōu)檫@個(gè)類(lèi)添加一個(gè)公共(public)域: private string[] StringsSet; 此外,,它還可能需要定義一些屬性,下面是定義一個(gè)只讀的屬性的例子: public int StringLength{ get { return StringsSet.Length; }} C#中的屬性更充分地體現(xiàn)了對(duì)象的封裝性,,不直接操作類(lèi)的數(shù)據(jù)內(nèi)容而是通過(guò)訪問(wèn)器進(jìn)行訪問(wèn),,它借助于get 和set訪問(wèn)器對(duì)屬性的值進(jìn)行讀寫(xiě)。而在C++中,這是需要程序員手工完成的一項(xiàng)工作,。 在屬性的訪問(wèn)聲明中: .只有set 訪問(wèn)器表明屬性的值只能進(jìn)行設(shè)置而不能讀出 .只有g(shù)et 訪問(wèn)器表明屬性的值是只讀的不能改寫(xiě) .同時(shí)具有set 訪問(wèn)器和get 訪問(wèn)器表明屬性的值的讀寫(xiě)都是允許的 你或許會(huì)發(fā)現(xiàn)域和屬性是如此相似,,確實(shí)屬性和域的語(yǔ)法比較類(lèi)似的,,但是它們是絕對(duì)不同的,區(qū)別在于你不能把屬性當(dāng)做變量那樣使用,,也不能把屬性作為引用型參數(shù)或輸出參數(shù)來(lái)進(jìn)行傳遞,,相反的,對(duì)于域,,沒(méi)有這些限制,。 下面的代碼定義了這個(gè)類(lèi)的構(gòu)造函數(shù): public StringComponent() 構(gòu)造函數(shù)必須與類(lèi)同名,它可以重載,,但是不能有返回值,,因此它也沒(méi)有返回值類(lèi)型前綴。當(dāng)用戶(hù)新建一個(gè)類(lèi)的實(shí)例時(shí),,構(gòu)造函數(shù)就會(huì)自動(dòng)執(zhí)行,,同時(shí),C#的垃圾收集機(jī)制開(kāi)始對(duì)這個(gè)實(shí)例進(jìn)行管理,,并且將在適當(dāng)?shù)臅r(shí)候回收資源,。 然后,我們編寫(xiě)了一個(gè)GetString()函數(shù),,這個(gè)函數(shù)根據(jù)用戶(hù)傳入的index值,,返回相應(yīng)的記錄: public string GetString(int index) { … return StringsSet[index];} 要注意的是其中的異常處理的方法: throw new IndexOutOfRangeException(); 作為一個(gè)健壯的組件,異常處理機(jī)制是不可或缺的,,雖然它可能會(huì)消耗掉一些資源,,但是它帶來(lái)的安全性的提升會(huì)使你覺(jué)得消耗的資源簡(jiǎn)直微不足道。這里使用了一個(gè)系統(tǒng)定義的異常類(lèi)IndexOutOfRangeException(),,事實(shí)上,,更多的情況是你必須自己定義異常類(lèi),以適應(yīng)各種不同的情況,。下面的代碼示例展示了如何定義一個(gè)異常類(lèi): public class MyApplicationException : ApplicationException{ public string AMsg; public MyApplicatonException(string strMsg) { AMsg=strMsg; } } 定義一個(gè)異常類(lèi)與定義普通的類(lèi)并沒(méi)有什么區(qū)別,,唯一的區(qū)別在于異常類(lèi)必須繼承自System.Exception類(lèi)。事實(shí)上,,微軟公司推薦把所有用戶(hù)自定義的異常類(lèi)作為ApplicationException類(lèi)的子類(lèi),。把類(lèi)MyApplicationException放到命名空間CompCS中,這樣你就可以改寫(xiě)GetString()函數(shù)中的異常處理方式,。下面是一個(gè)帶有更完善的異常處理機(jī)制的GetString()方法: public string GetString(int index) { try { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new MyApplicationException("參數(shù)超出范圍"); } } catch(MyApplicationException mErr) { Console.WriteLine(mErr.AMsg); } catch(Exception Err) { Console.WriteLine(Err.Message); } return StringsSet[index]; } 采用類(lèi)似這樣的方式,,你可以應(yīng)付比這復(fù)雜得多的情況。 下面,,我們來(lái)考慮給這個(gè)類(lèi)添加事件,。事件機(jī)制的引入使得開(kāi)發(fā)者可以更靈活地開(kāi)發(fā)程序。下面的代碼示例展示了如何定義一個(gè)事件: public event EventHandler Modified; 在C#中使用event關(guān)鍵字定義事件,。把這個(gè)定義放到我們的類(lèi)ComponentCS.StringComponent中,,然后我們添加一個(gè)函數(shù)Modify(),,這個(gè)函數(shù)修改字符數(shù)組StringsSet中指定位置的值,同時(shí)引發(fā)OnModify事件,,而在Modify事件中,,我們調(diào)用的是事件Modified所指定的函數(shù): public void Modify(int index,string value) { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new IndexOutOfRangeException(); } else { StringsSet[index]=value; OnModify(); } } private void OnModify() { EventArgs e=new EventArgs(); if(!(Modified==null)) Modified(this,e); } 然后我們可以用如下的方法調(diào)用: private void DoIt(){ StringComponent mysc=new StringComponent(); mysc.Modified+=new EventHandler(Called); mysc.Modify(2,"another string");}public void Called(object o,EventArgs e){ Console.WriteLine("Changed");} 在函數(shù)DoIt()中,我們首先建立了一個(gè)StringComponent類(lèi)的對(duì)象mysc,,然后將它的Mofidied事件關(guān)聯(lián)到Called()方法: mysc.Modified+=new EventHandler(Called); 注意“+=”符號(hào)的使用,,相反地,,如果使用“-=”符號(hào),,可以取消這個(gè)事件的綁定。 現(xiàn)在我們得到了一個(gè)雖然簡(jiǎn)單,,但是比較完整的組件類(lèi): using System; namespace ComponentCS { public class StringComponent { private string[] StringsSet; public event EventHandler Modified; public int StringLength { get { return StringsSet.Length; } } public void Modify(int index,string value) { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new IndexOutOfRangeException(); } else { StringsSet[index]=value; OnModify(); } } private void OnModify() { EventArgs e=new EventArgs(); if(!(Modified==null)) Modified(this,e); } public StringComponent() { StringsSet = new string[] { "C# String 0", "C# String 1", "C# String 2", "C# String 3" }; } public string GetString(int index) { if ((index 〈 0) || (index 〉= StringsSet.Length)) { throw new IndexOutOfRangeException(); } return StringsSet[index]; } } } 最后要做的就是把它編譯成.dll(動(dòng)態(tài)鏈接庫(kù))文件,,以便發(fā)布。發(fā)布成.dll文件最大的好處就是.dll文件中的內(nèi)容已經(jīng)編譯,,可以大大加快程序運(yùn)行速度,,此外還可以保護(hù)源代碼。 將產(chǎn)生的.cs文件編譯成為.dll文件的方法如下: csc.exe /t:library /debug+ /out:myCom.dll example.cs 這樣就輸出了名為myCom.dll的.dll文件,。 OK,我們已經(jīng)完成一個(gè)組件,,麻雀雖小,五臟俱全,,這就是一切組件的基礎(chǔ)了,,整個(gè)過(guò)程花不了十分鐘。 當(dāng)然,,如果是一個(gè)具備實(shí)際使用價(jià)值的組件,,我們要考慮的遠(yuǎn)遠(yuǎn)不止這些,但是可以看到,,C#對(duì)組件的強(qiáng)大支持,,可以大大提高我們的開(kāi)發(fā)效率,從而使我們有更多的精力放在算法設(shè)計(jì)等方面,,開(kāi)發(fā)出更加出色的組件,。 |
|