第一篇:C#面向?qū)ο髮W(xué)習(xí)心得
一、封裝
這是一種隱藏信息的特性。拿本節(jié)引例來說,類CalculateDate 將數(shù)據(jù)結(jié)構(gòu)與算法隱藏在類的內(nèi)部,外界使用者無需知道具體技術(shù)實(shí)現(xiàn)細(xì)節(jié)即可使用此類。封裝這一特性不僅大大提高了代碼的易用性,而且還使得類的開發(fā)者可以方便地更換新的算法,這種變化不會(huì)影響使用類的外部代碼。可以用以下公式展示類的封裝特性:封裝的類=數(shù)據(jù)+對(duì)此數(shù)據(jù)所進(jìn)行的操作(即算法)。通俗地說,封裝就是:包起外界不必需要知道的東西,只向外界展露可供展示的東西。在面向?qū)ο罄碚撝校庋b這個(gè)概念擁有更為寬廣的含義。小到一個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),大到一個(gè)完整的軟件子系統(tǒng),靜態(tài)的如某軟件系統(tǒng)要收集數(shù)據(jù)信息項(xiàng),動(dòng)態(tài)的如某個(gè)工作處理流程,都可以封裝到一個(gè)類中。具備這種“封裝”的意識(shí),是掌握面向?qū)ο蠓治雠c設(shè)計(jì)技巧的關(guān)鍵。
二、繼承
繼承是面向?qū)ο缶幊讨幸粋€(gè)非常重要的特性,它也是另一個(gè)重要特性——多態(tài)的基礎(chǔ)。現(xiàn)實(shí)生活中的事物都?xì)w屬于一定的類別。在一些書中,將父類稱為超類(super class)。“繼承”關(guān)系有時(shí)又稱為“派生”關(guān)系,“B 繼承自A”,可以說為“B 派生自A”,或反過來說,“A 派生出B”。父類與子類之間擁有以下兩個(gè)基本特性:
(1)是一種(IS-A)關(guān)系:子類是父類的一種特例。
(2)擴(kuò)充(Extends)關(guān)系:子類擁有父類所沒有的功能。
1.類成員的訪問權(quán)限
面向?qū)ο缶幊痰囊淮筇攸c(diǎn)就是可以控制類成員的可訪問性。當(dāng)前主流的面向?qū)ο笳Z言都擁有以下三種基本的可訪問性:
(1)公有 public 訪問不受限制。
(2)私有 private 只有類自身成員可以訪問。
(3)保護(hù) protected 子類可以訪問,其他類無法訪問。
由此可見,可以通過子類對(duì)象訪問其父類的所有公有成員,事實(shí)上,外界根本分不清楚對(duì)象的哪些公有成員來自父類,哪些公有成員來自子類自身。小結(jié)一下繼承條件下的類成員訪問權(quán)限:
(1)所有不必讓外人知道的東西都是私有的。
(2)所有需要向外提供的服務(wù)都是公有的。
(3)所有的“祖?zhèn)鹘^招”,“秘不外傳”的都是保護(hù)的。
C#中還有一種可訪問性,就是由關(guān)鍵字internal 所確定的“內(nèi)部”訪問性。internal 有點(diǎn)像public,外界類也可以直接訪問聲明為internal 的類或類的成員,但這只局限于同一個(gè)程序集內(nèi)部。
讀者可以簡(jiǎn)單地將程序集理解為一個(gè)獨(dú)立的DLL 或EXE 文件。一個(gè)DLL 或EXE 文件中可以有多個(gè)類,如果某個(gè)類可被同一程序集中的類訪問,但其他程序集中的類不能訪問它,則稱此類具有internal 訪問性。internal 是C#的默認(rèn)可訪問性,這就是說,如果某個(gè)類沒有任何可訪問性關(guān)鍵字在它前面,則它就是internal 的。
2.子類父類變量的相互賦值
子類對(duì)象可以被當(dāng)成基類對(duì)象使用。這是因?yàn)樽宇悓?duì)象本就是一種(IS_A)父類對(duì)象,因此,以下代碼是合法的:
Parent p;
Son c = new Son();
p = c;
然而,反過來就不可以,父類對(duì)象變量不可以直接賦值給子類變量。如果確信父類變量中所引用的對(duì)象的確是子類類型,則可以通過類型強(qiáng)制轉(zhuǎn)換進(jìn)行賦值,其語法格式為: 子類對(duì)象變量=(子類名稱)基類對(duì)象變量;
子類對(duì)象變量=基類對(duì)象變量 as 子類名稱;
3.方法重載、隱藏與虛方法調(diào)用
由于子類對(duì)象同時(shí)匯集了父類和子類的所有公共方法,而C#并未對(duì)子類和父類的方法名稱進(jìn)行過多限制,因此,一個(gè)問題出現(xiàn)了:如果子類中某個(gè)方法與父類方法的簽名一樣(即方法名和方法參數(shù)都一樣),那當(dāng)通過子類對(duì)象訪問此方法時(shí),訪問的是子類還是父類所定義的方法?讓我們先從子類方法與父類方法之間的關(guān)系說起。總的來說,子類方法與父類方法之間的關(guān)系可以概括為以下三種:
(1)擴(kuò)充(Extend):子類方法,父類沒有;
(2)重載(Overload):子類有父類的同名函數(shù),但參數(shù)類型或數(shù)目不一樣;
(3)完全相同:子類方法與父類方法從方法名稱到參數(shù)類型完全一樣。
當(dāng)子類與父類擁有完全一樣的方法時(shí),稱“子類隱藏了父類的同名方法,當(dāng)分別位于父類和子類的兩個(gè)方法完全一樣時(shí),調(diào)用哪個(gè)方法由對(duì)象變量的類型決定。“new”關(guān)鍵字明確告訴C#編譯器,子類隱藏父類的同名方法,提供自己的新版本。如果子類隱藏了父類的同名方法,要在子類方法的實(shí)現(xiàn)代碼中調(diào)用父類被隱藏的同名方法時(shí)要使用base 關(guān)鍵字。如果子類隱藏了父類的同名方法,不進(jìn)行強(qiáng)制轉(zhuǎn)換,就無法通過父類變量直接調(diào)用子類的同名方法,哪怕父類變量引用的是子類對(duì)象。這是不太合理的。我們希望每個(gè)對(duì)象都只干自己職責(zé)之內(nèi)的事,即如果父類變量引用的是子類對(duì)象,則調(diào)用的就是子類定義的方法,而如果父類變量引用的就是父類對(duì)象,則調(diào)用的是父類定義的方法。這就是說,希望每個(gè)對(duì)象都“各人自掃門前雪,莫管他人瓦上霜”。為達(dá)到這個(gè)目的,可以在父類同名方法前加關(guān)鍵字virtual,表明這是一個(gè)虛方法,子類可以重寫此方法:即在子類同名方法前加關(guān)鍵字override,表明對(duì)父類同名方法進(jìn)行了重寫。所以,將父類方法定義為虛方法,子類重寫同名方法之后,通過父類變量調(diào)用此方法,到底是調(diào)用父類還是子類的,由父類變量引用的真實(shí)對(duì)象類型決定,而與父類變量無關(guān)!很明顯,“虛方法調(diào)用”特性可以讓我們寫出非常靈活的代碼,大大減少由于系統(tǒng)功能
擴(kuò)充和改變所帶來的大量代碼修改工作量。由此給出結(jié)論:面向?qū)ο笳Z言擁有的“虛方法調(diào)用”特性,使我們可以只用同樣的一個(gè)語句,在運(yùn)行時(shí)根據(jù)對(duì)象類型而執(zhí)行不同的操作。
三、抽象
1.抽象類與抽象方法
在一個(gè)類前面加上“abstract”關(guān)鍵字,此類就成為了抽象類。對(duì)應(yīng)地,一個(gè)方法類前面加上“abstract”關(guān)鍵字,此方法就成為了抽象方法。注意抽象方法不能有實(shí)現(xiàn)代碼,在函數(shù)名后直接跟一個(gè)分號(hào)。抽象類專用于派生出子類,子類必須實(shí)現(xiàn)抽象類所聲明的抽象方法,否則,子類仍是抽象類。抽象類一般用于表達(dá)一種比較抽象的事物,而抽象方法則說明此抽象類應(yīng)該具有的某種性質(zhì),從同一抽象類中繼承的子類擁有相同的方法(即抽象類所定義的抽象方法),但這些方法的具體代碼每個(gè)類都可以不一樣。抽象類不能創(chuàng)建對(duì)象,一般用
它來引用子類對(duì)象。一個(gè)抽象類中可以包含非抽象的方法和字段。因此:包含抽象方法的類一定是抽象類,但抽象類中的方法不一定是抽象方法。除了方法可以是抽象的之外,屬性也可以是抽象的。
2.接口
接口可以看成是一種“純”的抽象類,它的所有方法都是抽象方法。抽象類定義了對(duì)象所屬的類別,而接口實(shí)際上定義了一種對(duì)象應(yīng)具有的行為特性。某個(gè)類可以實(shí)現(xiàn)多個(gè)接口,當(dāng)創(chuàng)建一個(gè)此類的對(duì)象之后,通過引用這個(gè)對(duì)象的對(duì)象變量可以訪問其所有的公有方法(包括自身的公有方法以及由接口定義的公有方法以)。在這種情況下,根本分不清哪些方法是由接口定義的,哪些是由類自己定義的。C#提供了一種“顯式接口”實(shí)現(xiàn)機(jī)制,可以區(qū)分開這兩種情況。由此得到一個(gè)結(jié)論:如果一個(gè)類顯式實(shí)現(xiàn)某個(gè)接口,則只能以此接口類型的變量為媒介調(diào)用此接口所定義的方法,而不允許通過類的對(duì)象變量直接調(diào)用。或者這樣說:被顯式實(shí)現(xiàn)的接口方法只能通過接口實(shí)例訪問,而不能通過類實(shí)例直接訪問。
四、多態(tài)
方法重載屬于多態(tài)的一種,兩個(gè)構(gòu)成重載關(guān)系的函數(shù)必須滿足幾個(gè)條件:函數(shù)名相同、參數(shù)類型不同,或參數(shù)個(gè)數(shù)不同。具體調(diào)用哪個(gè)方法要看參數(shù),需要注意的是,方法返回值類型的不同不是方法重載的判斷條件。多態(tài)編程的基本原理是:使用基類或接口變量編程。在多態(tài)編程中,基類一般都是抽象基類,其中擁有一個(gè)或多個(gè)抽象方法,各個(gè)子類可以根據(jù)需要重寫這些方法。或者使用接口,每個(gè)接口都規(guī)定了一個(gè)或多個(gè)抽象方法,實(shí)現(xiàn)接口的類根據(jù)需要實(shí)現(xiàn)這些方法。因此,多態(tài)的實(shí)現(xiàn)分為兩大基本類別:繼承多態(tài)和接口多態(tài)。
1.接口多態(tài)與繼承多態(tài)
接口多態(tài)與繼承多態(tài)其編程方式與作用都是類似的。但由于一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,所以,接口多態(tài)較繼承多態(tài)更靈活,因而在編程中也用得更廣。多態(tài)是面向?qū)ο蠹夹g(shù)中最精華的部分之一。大量的精巧軟件設(shè)計(jì)方案都建立在對(duì)多態(tài)特性的巧妙應(yīng)用上。在編程中應(yīng)用多態(tài),可以將其簡(jiǎn)化為兩句:應(yīng)用繼承實(shí)現(xiàn)對(duì)象的統(tǒng)一管理;應(yīng)用接口定義對(duì)象的行為特性。對(duì)比傳統(tǒng)的不使用多態(tài)的編程方式,使用多態(tài)的好處是:當(dāng)要修改程序并擴(kuò)充系統(tǒng)時(shí),需要修改的地方較少,對(duì)其他部分代碼的影響較小。
五、類與對(duì)象
類是面向?qū)ο缶幊痰幕締卧c使用C語言等結(jié)構(gòu)化編程語言不一樣,使用C#編程,所有的程序代碼幾乎都放在類中,不存在獨(dú)立于類之外的函數(shù)。一個(gè)類可以包含兩種成員:靜態(tài)成員和實(shí)例成員,靜態(tài)成員是供類的所有對(duì)象所共享的,而實(shí)例成員只供某一個(gè)對(duì)象所有。實(shí)例成員與靜態(tài)成員的訪問規(guī)則:位于同一類中的實(shí)例方法可直接相互調(diào)用;類的字段(包括實(shí)例字段和靜態(tài)字段)可以被同一類中的所有實(shí)例方法直接訪問;類中的靜態(tài)方法只能直接訪問類靜態(tài)字段。
類中包括:方法和字段,屬性是一種特殊的字段,它可以保證數(shù)據(jù)的合法性,方法和字段這兩個(gè)概念是面向?qū)ο罄碚摰男g(shù)語,是通用于各種面向?qū)ο笳Z言的。字段(Field)代表了類中的數(shù)據(jù),在類的所有方法之外定義一個(gè)變量即定義了一個(gè)字段。在變量之前可以加上public、private 和protected 表示字段的訪問權(quán)限。方法(function)功能代碼的集合,在程序開發(fā)過程中,經(jīng)常發(fā)現(xiàn)多處需要實(shí)現(xiàn)或調(diào)用某一個(gè)公用功能,這些功能的實(shí)現(xiàn)都需要書
寫若干行代碼。如果在調(diào)用此功能的地方重復(fù)書寫這些功能代碼,將會(huì)使整個(gè)程序中代碼大量重復(fù),會(huì)增大開發(fā)工作量,增加代碼維護(hù)的難度。為了解決代碼重復(fù)的問題,絕大多數(shù)程序設(shè)計(jì)語言都將完成某一公用功能的多個(gè)語句組合在一起,起一個(gè)名字用于代表這些語句的全體,這樣的代碼塊被稱為“函數(shù)(function)”。引入“函數(shù)”概念之后,程序中凡需要調(diào)用此公用功能的地方都可以只寫出函數(shù)名,此名字就代表了函數(shù)中所包含的所有代碼,這樣一來,就不再需要在多個(gè)地方重復(fù)書寫這些功能代碼。
對(duì)象是以類為模板創(chuàng)建出來的。類與對(duì)象之間是一對(duì)多的關(guān)系。在C#中,使用new 關(guān)鍵字創(chuàng)建對(duì)象。在程序中“活躍”的是對(duì)象而不是類。在面向?qū)ο箢I(lǐng)域,對(duì)象有時(shí)又被稱為是“類的實(shí)例”,“對(duì)象”與“類的實(shí)例”這兩個(gè)概念是等同的。
六、值類型與引用類型
1.值類型
值類型變量與引用類型變量的內(nèi)存分配模型也不一樣。每個(gè)正在運(yùn)行的程序都對(duì)應(yīng)著一個(gè)進(jìn)程(process),在一個(gè)進(jìn)程內(nèi)部,可以有一個(gè)或多個(gè)線程(thread),每個(gè)線程都擁有一塊“自留地”,稱為“線程堆棧”,大小為1M,用于保存自身的一些數(shù)據(jù),比如函數(shù)中定義的局部變量、函數(shù)調(diào)用時(shí)傳送的參數(shù)值等,這部分內(nèi)存區(qū)域的分配與回收不需要程序員干涉。所有值類型的變量都是在線程堆棧中分配的。值類型共有三種:簡(jiǎn)單類型、枚舉類型和結(jié)構(gòu)類型。
2.引用類型
另一塊內(nèi)存區(qū)域稱為“堆(heap)”,在.NET 這種托管環(huán)境下,堆由CLR 進(jìn)行管理,所以又稱為“托管堆(managed heap)”。用new 關(guān)鍵字創(chuàng)建的類的對(duì)象時(shí),分配給對(duì)象的內(nèi)存單元就位于托管堆中。在程序中我們可以隨意地使用new 關(guān)鍵字創(chuàng)建多個(gè)對(duì)象,因此,托管堆中的內(nèi)存資源是可以動(dòng)態(tài)申請(qǐng)并使用的,當(dāng)然用完了必須歸還。打個(gè)比方更易理解:托管堆相當(dāng)于一個(gè)旅館,其中的房間相當(dāng)于托管堆中所擁有的內(nèi)存單元。當(dāng)程序員用new 方法創(chuàng)建對(duì)象時(shí),相當(dāng)于游客向旅館預(yù)訂房間,旅館管理員會(huì)先看一下有沒有合適的空房間,有的話,就可以將此房間提供給游客住宿。當(dāng)游客旅途結(jié)束,要辦理退房手續(xù),房間又可以為其他旅客提供服務(wù)了。引用類型共有四種:類類型、接口類型、數(shù)組類型和委托類型。所有引用類型變量所引用的對(duì)象,其內(nèi)存都是在托管堆中分配的。嚴(yán)格地說,我們常說的“對(duì)象變量”其實(shí)是類類型的引用變量。但在實(shí)際中人們經(jīng)常將引用類型的變量簡(jiǎn)稱為“對(duì)象變量”,用它來指代所有四種類型的引用變量。
七、命名空間與類庫
1.命名空間
在使用面向?qū)ο蠹夹g(shù)開發(fā)的現(xiàn)代軟件系統(tǒng)中,經(jīng)常擁有數(shù)百甚至上千個(gè)類,為了方便地管理這些類,面向?qū)ο蠹夹g(shù)引入了“命名空間(namespace)”的概念。命名空間可以看成是類的“容器”,它可以包含多個(gè)類。.NET Framework 使用命名空間來管理所有的類。如果把類比喻成書的話,則命名空間類似于放書的書架,書放在書架上,類放在命名空間里。當(dāng)我們?nèi)D書館查找一本書時(shí),需要指定這本書的編號(hào),編號(hào)往往規(guī)定了書放在哪個(gè)書庫的哪個(gè)書架上,通過逐漸縮小的范圍:圖書館->書庫->書架,最終可以在某個(gè)書架中找到這本書。類似地,可以采用圖書館保存圖書類似的方法來管理類,通過逐漸縮小的范圍:最大的命名空間->子命名空間->孫命名空間??,最終找到一個(gè)類。
2.類庫
為了提高軟件開發(fā)的效率,人們?cè)谡麄€(gè)軟件開發(fā)過程中大量應(yīng)用了軟件工程的模塊化原則,將可以在多個(gè)項(xiàng)目中使用的代碼封裝為可重用的軟件模塊,其于這些可復(fù)用的軟件模塊,再開發(fā)新項(xiàng)目就成為“重用已有模塊,再開發(fā)部分新模塊,最后將新舊模塊組裝起來”的過程。整個(gè)軟件開發(fā)過程類似于現(xiàn)代工業(yè)的生產(chǎn)流水線,生產(chǎn)線上的每個(gè)環(huán)節(jié)都由特定的人員負(fù)責(zé),整個(gè)生產(chǎn)線上的工作人員既分工明確又相互合作,大大地提高了生產(chǎn)效率。在組件化開發(fā)大行其道的今天,人們通常將可以重用的軟件模塊稱為“軟件組件”。在全面向?qū)ο蟮?NET 軟件平臺(tái)之上,軟件組件的表現(xiàn)形式為程序集(Assembly),可以通過在Visual Studio 中創(chuàng)建并編譯一個(gè)類庫項(xiàng)目得到一個(gè)程序集。在Visual Studio 的項(xiàng)目模板中,可以很方便地創(chuàng)建類庫(Class Library)項(xiàng)目,Visual Studio 會(huì)自動(dòng)在項(xiàng)目中添加一個(gè)名為Class1.cs 的類文件,程序員可在此類文件中書寫代碼,或者添加新的類。一個(gè)類庫項(xiàng)目中可以容納的類數(shù)目沒有限制,但只有聲明為public 的類可以被外界使用。類庫項(xiàng)目編譯之后,會(huì)生成一個(gè)動(dòng)態(tài)鏈接庫(DLL:Dynamic Link Library)文件。這就是可以被重用的.NET 軟件組件——程序集。默認(rèn)情況下,類庫文件名就是項(xiàng)目名加上“.dll”后綴。每個(gè)類庫項(xiàng)目都擁有一個(gè)默認(rèn)的命名空間,可以通過類庫項(xiàng)目的屬性窗口來指定。需要仔細(xì)區(qū)分“類庫項(xiàng)目”、“程序集”和“命名空間”這三個(gè)概念的區(qū)別:
(1)每個(gè)類庫項(xiàng)目編譯之后,將會(huì)生成一個(gè)程序集。
(2)類庫項(xiàng)目中可以擁有多個(gè)類,這些類可屬于不同的命名空間。
(3)不同的類庫項(xiàng)目可以定義相同的命名空間。
根據(jù)上述三個(gè)特性,可以得到以下結(jié)論:“命名空間”是一個(gè)邏輯上的概念,它的物理載體是“程序集”,具體體現(xiàn)為“DLL”(或EXE)文件。在Visual Studio 中,可通過創(chuàng)建“類庫”類型的項(xiàng)目生成程序集。一個(gè)程序集可以有多個(gè)命名空間,而一個(gè)命名空間也可以分布于多個(gè)程序集。一旦生成了一個(gè)程序集,在其他項(xiàng)目中就可以通過添加對(duì)這一程序集的引用而使用此程序集中的類。其方法是在“項(xiàng)目”菜單中選擇“添加程序集”命令,激活“瀏覽”卡片,選擇一個(gè)現(xiàn)有的程序集文件(DLL 或EXE)。一個(gè)項(xiàng)目添加完對(duì)特定程序集的引用之后,就可以直接創(chuàng)建此程序集中的類了,當(dāng)然要注意指明其命名空間。
八、委托
委托是一種新的面向?qū)ο笳Z言特性,在歷史比較長的面向?qū)ο笳Z言比如C++中并未出現(xiàn)過。微軟公司在設(shè)計(jì)運(yùn)行于.NET Framework平臺(tái)之上的面向?qū)ο笳Z言(如C#和VisualBasic.NET)時(shí)引入了這一新特性。委托(delegate)也可以看成是一種數(shù)據(jù)類型,可以用于定義變量。但它是一種特殊的數(shù)據(jù)類型,它所定義的變量能接收的數(shù)值只能是一個(gè)函數(shù),更確切地說,委托類型的變量可以接收一個(gè)函數(shù)的地址,很類似于C++語言的函數(shù)指針。簡(jiǎn)單地說:委托變量可看成是一種類型安全的函數(shù)指針,它只能接收符合其要求的函數(shù)地址。委托可以看成是一個(gè)函數(shù)的“容器”,將某一具體的函數(shù)“裝入”后,就可以把它當(dāng)成函數(shù)一樣使用。定義委托類型時(shí)對(duì)函數(shù)的要求被稱為函數(shù)的“簽名(signature)”。函數(shù)的簽名規(guī)定了函數(shù)的參數(shù)數(shù)目和類型,以及函數(shù)的返回值,體現(xiàn)了函數(shù)的本質(zhì)特征。每一個(gè)委托都確定了一個(gè)函數(shù)的簽名。擁有不同簽名的函數(shù)不能賦值給同一類型的委托變量。因此,一個(gè)委托類型的變量,可以引用任何一個(gè)滿足其要求的函數(shù)。
1.委托的組合與分解
委托變量可以代表某一函數(shù),使用委托變量就相當(dāng)于調(diào)用一個(gè)函數(shù)。如果僅是這么簡(jiǎn)單,那么直接調(diào)用函數(shù)不就行了嗎?為什么還要引入“委托”這一特性?事實(shí)上,委托不僅可以代表一個(gè)函數(shù),還可以組合“一堆”的函數(shù),然后批量執(zhí)行它們,這樣的委托變量又稱為“多路委托變量”。可以用加法運(yùn)算符來組合單個(gè)委托變量為多路委托變量。類似地,也可以使用減法運(yùn)算符來從一個(gè)多路委托變量中移除某個(gè)委托變量。
2.事件與多路委托
事件的主要特點(diǎn)是一對(duì)多關(guān)聯(lián),即一個(gè)事件源,多個(gè)響應(yīng)者。在具體技術(shù)上,.NET Framework 的事件處理機(jī)制是基于多路委托實(shí)現(xiàn)的。事件與多路委托其實(shí)大同小異,只不過多路委托允許在事件源對(duì)象之外激發(fā)事件罷了。所有的.NET Framework 可視化窗體控件的預(yù)定義事件,都是某一對(duì)應(yīng)的“事件名+Handler”委托類型的變量。與此事件相關(guān)的信息都封裝在“事件名+Args”類型的事件參數(shù)中,此事件參數(shù)有一個(gè)基類EventArgs,它是所有事件參數(shù)的基類。明了上述內(nèi)部機(jī)理,對(duì)于我們?cè)诔绦蛑卸x自己的事件非常有好處,尤其是開發(fā)一個(gè)自定義的可視化控件時(shí),如果需要增加新的事件類型,我們應(yīng)盡量遵循.NET Framework 的定義事件的框架,給事件取一個(gè)名字,定義一個(gè)“事件名+Handler”的事件委托類型,再從EventArgs 派生出自定義事件的參數(shù),取名為“事件名+Args”。
面向?qū)ο蟮能浖到y(tǒng)有許多都是事件驅(qū)動(dòng)的,ASP.NET 就采用了“事件驅(qū)動(dòng)”的編程方式。所謂“事件驅(qū)動(dòng)”的開發(fā)方式,就是指整個(gè)系統(tǒng)包含許多的對(duì)象,這些對(duì)象可以引發(fā)多種事件,軟件工程師的主要開發(fā)工作就是針對(duì)特定的事件書寫代碼響應(yīng)它們。.NET 事件處理機(jī)制建立在委托的基礎(chǔ)之上,而這兩者都是ASP.NET 技術(shù)的基礎(chǔ)之一。因此,必須牢固地掌握好委托和事件這兩種編程技術(shù),才能為掌握ASP.NET 技術(shù)掃清障礙。
第二篇:學(xué)習(xí)心得《面向?qū)ο蟆?/a>
面向?qū)ο笳n程學(xué)習(xí)心得
這學(xué)期的面向?qū)ο笳n程對(duì)我來說是收獲匪淺的一門課。通過老師課件的講解,自己一些相關(guān)書籍的閱讀和實(shí)踐作業(yè)的完成,逐步對(duì)課程有了由淺及深的認(rèn)識(shí)。
面向?qū)ο?Object Oriented,OO)是一門以實(shí)踐為主課程,課程中可以分開兩塊OOA(面向?qū)ο笙到y(tǒng)分析)和OOD(面向?qū)ο笙到y(tǒng)設(shè)計(jì))。OOA(面向?qū)ο笙到y(tǒng)分析)主要內(nèi)容: 研究問題域和用戶需求,運(yùn)用面向?qū)ο蟮挠^點(diǎn)和原則發(fā)現(xiàn)問題域中與系統(tǒng)責(zé)任有關(guān)的對(duì)象,以及對(duì)象的特征和相互關(guān)系.OOA不涉及針對(duì)具體實(shí)現(xiàn)采取的設(shè)計(jì)決策和有關(guān)細(xì)節(jié),獨(dú)立于具體實(shí)現(xiàn)的系統(tǒng)模型。是一個(gè)完整確切反映問題域和用戶需求的系統(tǒng)模型。OOA的優(yōu)勢(shì):復(fù)用、可擴(kuò)展、可維護(hù)性、彈性。
OOD(面向?qū)ο笙到y(tǒng)設(shè)計(jì)):以O(shè)OA模型為基礎(chǔ),按照實(shí)現(xiàn)的要求進(jìn)行設(shè)計(jì)決策,包括全局性的決策和局部細(xì)節(jié)的設(shè)計(jì),與具體的實(shí)現(xiàn)條件相關(guān)。OOD的步驟:細(xì)化重組類→細(xì)化和實(shí)現(xiàn)類之間的關(guān)系,明確其可見性→增加屬性,指定屬性的類型和可見性→分配職責(zé),定義執(zhí)行每個(gè)職責(zé)的方法→對(duì)消息驅(qū)動(dòng)的系統(tǒng),明確消息傳遞的方式→利用設(shè)計(jì)模式進(jìn)行局部設(shè)計(jì)→畫出詳細(xì)的類圖和時(shí)序圖。
面向?qū)ο蟮姆治雠c設(shè)計(jì)方法將致力于解決傳統(tǒng)軟件研發(fā)過程中由于軟件模塊化結(jié)構(gòu)化程度不高帶來的軟件重用性差、軟件可維護(hù)性差、開發(fā)出的軟件不能滿足用戶需要等方面問題。面向?qū)ο蟮母拍畎ǎ簩?duì)象、對(duì)象的狀態(tài)和行為、類、類的結(jié)構(gòu)、消息和方法。對(duì)象概念將包含對(duì)象唯一性、抽象性、繼承性、多態(tài)性的重要特征。面向?qū)ο蟮囊匕撼橄蟆⒎庋b性、共享性三方面。
在設(shè)計(jì)模式的研究過程中,我們組選擇的是迭代器(Iterator)的設(shè)計(jì)模式研究。完成設(shè)計(jì)研究后,我對(duì)迭代器的設(shè)計(jì)模式有了更為深刻的理解。迭代器(Iterator)提供一個(gè)方法順序訪問一個(gè)聚合對(duì)象的各個(gè)元素,而又不暴露該對(duì)象的內(nèi)部表示。并了解到迭代器設(shè)計(jì)模式一般在以下三類場(chǎng)合使用較多。
? 訪問一個(gè)聚合對(duì)象的內(nèi)容而無需暴露它的內(nèi)部表示。? 支持對(duì)聚合對(duì)象的多種遍歷。因?yàn)楸闅v狀態(tài)是保存在每一個(gè)迭代器對(duì)象中的。
? 為遍歷不同的聚合結(jié)構(gòu)提供一個(gè)統(tǒng)一的接口。根據(jù)實(shí)現(xiàn)方式的不同,效果上會(huì)有差別。同時(shí)還簡(jiǎn)化了容器的接口。但是在java Collection中為了提高可擴(kuò)展性,容器還是提供了遍歷的接口。在面向?qū)ο蟮能浖O(shè)計(jì)中,我們經(jīng)常會(huì)遇到一類集合對(duì)象,這類集合對(duì)象的內(nèi)部結(jié)構(gòu)可能有著各種各樣的實(shí)現(xiàn),但是歸結(jié)起來,無非有兩點(diǎn)是需要我們?nèi)リP(guān)心的:一是集合內(nèi)部的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu),二是遍歷集合內(nèi)部的數(shù)據(jù)。面向?qū)ο笤O(shè)計(jì)原則中有一條是類的單一職責(zé)原則,所以我們要盡可能的去分解這些職責(zé),用不同的類去承擔(dān)不同的職責(zé)。Iterator模式就是分離了集合對(duì)象的遍歷行為,抽象出一個(gè)迭代器類來負(fù)責(zé),這樣既可以做到不暴露集合的內(nèi)部結(jié)構(gòu),又可讓外部代碼透明的訪問集合內(nèi)部的數(shù)據(jù)。
在Java Collection的應(yīng)用中,提供的具體迭代器角色是定義在容器角色中的內(nèi)部類。這樣便保護(hù)了容器的封裝。但是同時(shí)容器也提供了遍歷算法接口,你可以擴(kuò)展自己的迭代器。至于迭代器模式的使用。客戶程序要先得到具體容器角色,然后再通過具體容器角色得到具體迭代器角色。這樣便可以使用具體迭代器角色來遍歷容器了。
OOA和OOD之間沒有明顯的界限。OOA與OOD的不可分割性正好說明了OO思想的強(qiáng)大,即軟件過程階段的無縫連接,在交流與溝通中不會(huì)產(chǎn)生鴻溝,這是相對(duì)結(jié)構(gòu)化思想的好處,因?yàn)閺墓δ苣K到某塊詳細(xì)控制邏輯設(shè)計(jì)兩者之間的聯(lián)系不是十分緊密,需要分析人員與設(shè)計(jì)人員的再溝通。
通過課程的學(xué)習(xí)與實(shí)踐,對(duì)面向?qū)ο蟮睦砟睿约跋嚓P(guān)方法,設(shè)計(jì)模式有了更為深刻的理解與掌握。針對(duì)面向?qū)ο蟮姆治雠c設(shè)計(jì)課程的授課內(nèi)容及方法,我個(gè)人覺得對(duì)我還是有不少的幫助和 提高。結(jié)合自己的工作,雖然與開發(fā)接觸的比較少,但是在運(yùn)維過程中,如果能了解開發(fā)原理,結(jié)合實(shí)際的工作,會(huì)對(duì)一些源代碼的分析能力以及工作效率的提高起到明顯的幫助作用。
第三篇:java面向?qū)ο蟮膶W(xué)習(xí)心得
Java面向?qū)ο蟮膶W(xué)習(xí)心得
大三的時(shí)候?qū)W校組織我們?nèi)ヌK州NIIT參加四個(gè)月的java實(shí)訓(xùn),我開始系統(tǒng)的學(xué)習(xí)期java,之前大學(xué)的時(shí)候?qū)W的比較寬泛,沒有專門的正對(duì)java的學(xué)習(xí)。
首先我是從學(xué)習(xí)Java編程開始接觸OOP(面向?qū)ο缶幊?,剛開始使用Java編寫程序的時(shí)候感覺很別扭,因?yàn)槲以缫粤?xí)慣用C來編寫程序,很欣賞C的簡(jiǎn)潔性和高效性,喜歡C簡(jiǎn)練而表達(dá)能力豐富的風(fēng)格,特別忍受不了Java運(yùn)行起來慢吞吞的速度,相對(duì)冗長的代碼,而且一個(gè)很簡(jiǎn)單的事情,要寫好多類,一個(gè)類調(diào)用一個(gè)類,心里的抵觸情緒很強(qiáng)。
我對(duì)Java的面向?qū)ο蟮奶匦宰聊チ季茫哉J(rèn)為有所領(lǐng)悟,也開始有意識(shí)的運(yùn)用OOP風(fēng)格來寫程序,然而還是經(jīng)常會(huì)覺得不知道應(yīng)該怎樣提煉類,面對(duì)一個(gè)具體的問題的時(shí)候,會(huì)覺得腦子里千頭萬緒的,不知道怎么下手,一不小心,又會(huì)回到原來的思路上去。
舉個(gè)例子,要發(fā)廣告郵件,廣告郵件列表存在數(shù)據(jù)庫里面。倘若用C來寫的話,一般會(huì)這樣思考,先把郵件內(nèi)容讀入,然后連接數(shù)據(jù)庫,循環(huán)取郵件地址,調(diào)用本機(jī)的qmail的sendmail命令發(fā)送。
然后考慮用Java來實(shí)現(xiàn),既然是OOP,就不能什么代碼都塞到main過程里面,于是就設(shè)計(jì)了三個(gè)類:
一個(gè)類是負(fù)責(zé)讀取數(shù)據(jù)庫,取郵件地址,調(diào)用qmail的sendmail命令發(fā)送; 一個(gè)類是讀郵件內(nèi)容,MIME編碼成HTML格式的,再加上郵件頭;
一個(gè)主類負(fù)責(zé)從命令讀參數(shù),處理命令行參數(shù),調(diào)用發(fā)email的類。
把一件工作按照功能劃分為3個(gè)模塊分別處理,每個(gè)類完成一件模塊任務(wù)。
仔細(xì)的分析一下,就會(huì)發(fā)現(xiàn)這樣的設(shè)計(jì)完全是從程序員實(shí)現(xiàn)程序功能的角度來設(shè)計(jì)的,或者說,設(shè)計(jì)類的時(shí)候,是自低向上的,從機(jī)器的角度到現(xiàn)實(shí)世界的角度來分析問題的。因此在設(shè)計(jì)的時(shí)候,就已經(jīng)把程序編程實(shí)現(xiàn)的細(xì)節(jié)都考慮進(jìn)去了,企圖從底層實(shí)現(xiàn)程序這樣的出發(fā)點(diǎn)來達(dá)到滿足現(xiàn)實(shí)世界的軟件需求的目標(biāo)。
這樣的分析方法其實(shí)是不適用于Java這樣面向?qū)ο蟮木幊陶Z言,因?yàn)椋绻挠肅語言,封裝兩個(gè)C函數(shù),都會(huì)比Java實(shí)現(xiàn)起來輕松的多,邏輯上也清楚的多。
我覺得面向?qū)ο蟮木柙谟诳紤]問題的思路是從現(xiàn)實(shí)世界的人類思維習(xí)慣出發(fā)的,只要領(lǐng)會(huì)了這一點(diǎn),就領(lǐng)會(huì)了面向?qū)ο蟮乃季S方法。
舉一個(gè)非常簡(jiǎn)單的例子:假使現(xiàn)在需要寫一個(gè)網(wǎng)頁計(jì)數(shù)器,客戶訪問一次頁面,網(wǎng)頁計(jì)數(shù)器加1,計(jì)數(shù)器是這樣來訪問的后臺(tái)有一個(gè)數(shù)據(jù)庫表,保存每個(gè)id(一個(gè)id對(duì)應(yīng)一個(gè)被統(tǒng)計(jì)訪問次數(shù)的頁面)的計(jì)數(shù)器當(dāng)前值,請(qǐng)求頁面一次,對(duì)應(yīng)id的計(jì)數(shù)器的字段加1(這里我們忽略并發(fā)更新數(shù)據(jù)庫
表,出現(xiàn)的表鎖定的問題)。
如果按照一般從程序?qū)崿F(xiàn)的角度來分析,我們會(huì)這樣考慮:首先是從HTTP GET請(qǐng)求取到id,然后按照id查數(shù)據(jù)庫表,獲得某id對(duì)應(yīng)的訪問計(jì)數(shù)值,然后加1,更新數(shù)據(jù)庫,最后向頁面顯示訪問計(jì)數(shù)。
現(xiàn)在假設(shè)一個(gè)沒有程序設(shè)計(jì)經(jīng)驗(yàn)的人,他會(huì)怎樣來思考這個(gè)問題的呢?他會(huì)提出什么樣的需求呢?他很可能會(huì)這樣想:
我需要有一個(gè)計(jì)數(shù)器,這個(gè)計(jì)數(shù)器應(yīng)該有這樣的功能,刷新一次頁面,訪問量就會(huì)加1,另外最好還有一個(gè)計(jì)數(shù)器清0的功能,當(dāng)然計(jì)數(shù)器如果有一個(gè)可以設(shè)為任意值的功能的話,我就可以作弊了。
做為一個(gè)沒有程序設(shè)計(jì)經(jīng)驗(yàn)的人來說,他完全不會(huì)想到對(duì)數(shù)據(jù)庫應(yīng)該如何操作,對(duì)于HTTP變量該如何傳遞,他考慮問題的角度就是我有什么需求,我的業(yè)務(wù)邏輯是什么,軟件應(yīng)該有什么功能。
按照這樣的思路(請(qǐng)注意,他的思路其實(shí)就是我們平時(shí)在生活中習(xí)慣的思維方式),我們知道需要有一個(gè)計(jì)數(shù)器類 Counter,有一個(gè)必須的和兩個(gè)可選的方法:
getCount()// 取計(jì)數(shù)器值方法
resetCounter()// 計(jì)數(shù)器清0方法
setCount()// 設(shè)計(jì)數(shù)器為相應(yīng)的值方法
把Counter類完整的定義如下:
public class Counter {
public int getCount(int id){}
public void resetCounter(int id){}
public void setCount(int id, int currentCount){}
}
解決問題的框架已經(jīng)有了,來看一下如何使用Counter。在count.cgi里面調(diào)用Counter來計(jì)數(shù),程序片斷如下:
// 這里從HTTP環(huán)境里面取id值
...Counter myCounter = new Counter();// 獲得計(jì)數(shù)器
int currentCount = myCounter.getCount(id);// 從計(jì)數(shù)器中取計(jì)數(shù)
// 這里向客戶瀏覽器輸出
...程序的框架全都寫好了,剩下的就是實(shí)現(xiàn)Counter類方法里面具體的代碼了,此時(shí)才去考慮具體的程序語言實(shí)現(xiàn)的細(xì)節(jié),比如,在getCount()方法里面訪問數(shù)據(jù)庫,更新計(jì)數(shù)
值。
從上面的例子中看到,面向?qū)ο蟮乃季S方法其實(shí)就是我們?cè)诂F(xiàn)實(shí)生活中習(xí)慣的思維方式,是從人類考慮問題的角度出發(fā),把人類解決問題的思維方式逐步翻譯成程序能夠理解的思維方式的過程,在這個(gè)翻譯的過程中,軟件也就逐步被設(shè)計(jì)好了。
在運(yùn)用面向?qū)ο蟮乃季S方法進(jìn)行軟件設(shè)計(jì)的過程中,最容易犯的錯(cuò)誤就是開始分析的時(shí)候,就想到了程序代碼實(shí)現(xiàn)的細(xì)節(jié),因此封裝的類完全是基于程序?qū)崿F(xiàn)邏輯,而不是基于解決問題的業(yè)務(wù)邏輯。
學(xué)習(xí)JDBC編程的經(jīng)典錯(cuò)誤問法是:“我怎樣封裝對(duì)數(shù)據(jù)庫的select操作?”
面向?qū)ο蟮脑O(shè)計(jì)是基于解決業(yè)務(wù)問題的設(shè)計(jì),而不是基于具體編程技術(shù)的設(shè)計(jì)。我不會(huì)去封裝select語句的,我只封裝解決問題的業(yè)務(wù)邏輯,對(duì)數(shù)據(jù)庫的讀取是在業(yè)務(wù)邏輯的編碼實(shí)現(xiàn)階段才去考慮的問題。
回過頭看上面那個(gè)發(fā)廣告郵件的例子,應(yīng)該如何應(yīng)用面向?qū)ο蟮乃季S方法呢?
對(duì)于一個(gè)郵件來說,有郵件頭,郵件體,和郵件地址這三個(gè)屬性,發(fā)送郵件,需要一個(gè)發(fā)送的方法,另外還需要一個(gè)能把所有郵件地址列出來的方法。所以應(yīng)該如下設(shè)計(jì):
類JunkMail
屬性:
head
body
address
方法:
sendMail()// 發(fā)送郵件
listAllMail()// 列郵件地址
用Java來表示:
public class JunkMail {
private String head;
private String body;
private String address;
public JunkMain(){ // 默認(rèn)的類構(gòu)造器
// 從外部配置文件讀郵件頭和郵件體
this.head=...;
this.body=...;
}
public static boolean sendMail(String address){
// 調(diào)用qmail,發(fā)送email
}
public static Collection listAllMail(){
// 訪問數(shù)據(jù)庫,返回一個(gè)郵件地址集合}
}
當(dāng)把JunkMail設(shè)計(jì)好了以后,再調(diào)用JunkMail類完成郵件的發(fā)送,將是非常輕松的事情。
如果說傳統(tǒng)的面向過程的編程是符合機(jī)器運(yùn)行指令的流程的話,那么面向?qū)ο蟮乃季S方法就是符合現(xiàn)實(shí)生活中人類解決問題的思維過程。
在面向?qū)ο蟮能浖治龊驮O(shè)計(jì)的時(shí)候,要提醒自己,不要一上來就去想程序代碼的實(shí)現(xiàn),應(yīng)該拋開具體編程語言的束縛,集中精力分析我們要實(shí)現(xiàn)的軟件的業(yè)務(wù)邏輯,分析軟件的業(yè)務(wù)流程,思考應(yīng)該如何去描述和實(shí)現(xiàn)軟件的業(yè)務(wù)。畢竟軟件只是一個(gè)載體,業(yè)務(wù)才是我們真正要實(shí)現(xiàn)的目標(biāo)。
但是在設(shè)計(jì)過程中,心里卻往往在擔(dān)心,如果我完全不去考慮程序代碼的實(shí)現(xiàn)的話,那么我怎么知道我的設(shè)計(jì)一定合理呢?我怎么知道我設(shè)計(jì)的類、接口一定可以實(shí)現(xiàn)呢?所以經(jīng)常可以看到的現(xiàn)象就是:
在設(shè)計(jì)過程中,雖然知道不能過早考慮代碼實(shí)現(xiàn),但是每設(shè)計(jì)一個(gè)類,一個(gè)接口,心里都要不知不覺的用自己熟悉的編程語言大概的評(píng)估一下,看看能否編出來,因此,一不小心,就會(huì)又回到按照程序功能實(shí)現(xiàn)的思路進(jìn)行設(shè)計(jì)的老路上去了。
舉個(gè)例子來說明,在做Web程序設(shè)計(jì)的時(shí)候,經(jīng)常要遇到分頁顯示數(shù)據(jù)的情況。比如說需要把系統(tǒng)中所有的用戶都列出來這樣的功能。假設(shè)使用User類來表示用戶,增加用戶addUser(),刪除用戶deleteUser(),查詢所有用戶listUsers()方法。而數(shù)據(jù)庫中有一個(gè)user表,一條記錄是一個(gè)用戶的信息。下面考慮一下User類的方法的實(shí)現(xiàn):
addUser()和deleteUser()方法都好實(shí)現(xiàn),就是對(duì)數(shù)據(jù)庫增加記錄和刪除記錄。對(duì)于listUsers()方法,其實(shí)就是對(duì)user表的select,取出一個(gè)記錄集。但是該怎么從listUsers()方法中得到所有用戶的列表呢?
一個(gè)方法調(diào)用的返回值只有一個(gè),沒有多個(gè),所以很多情況下采用的辦法就是返回值定義為集合類型,比如Vector。這樣就可以在listUsers()方法的具體代碼實(shí)現(xiàn)的時(shí)候,從數(shù)據(jù)庫依次取出一個(gè)個(gè)記錄,插入到Vector里面來。在主程序里面,調(diào)用listUsers()方法可以返回一個(gè)Vector,然后再對(duì)Vector遍歷操作,就可以得到用戶列表了。
public class User {
public static void addUser(...){
// 數(shù)據(jù)庫insert一條記錄
}
public static void deleteUser(...){
// 數(shù)據(jù)庫delete一條記錄
}
public Vector listUsers(...){
// 數(shù)據(jù)庫select結(jié)果放到一個(gè)集合里面
}
}
這樣的設(shè)計(jì)基本合理,但是仍然有點(diǎn)小問題。因?yàn)樵谠O(shè)計(jì)的時(shí)候,就考慮到了用Java的集合類Vector來實(shí)現(xiàn)對(duì)不定長數(shù)據(jù)集的存放,因而違反了面向?qū)ο笤O(shè)計(jì)的一個(gè)原則:在設(shè)計(jì)的時(shí)候不應(yīng)過早的考慮具體程序語言的實(shí)現(xiàn)。所以必須用抽象的方法,和具體實(shí)現(xiàn)無關(guān)的方法來表達(dá)業(yè)務(wù)邏輯。
我們知道,通常對(duì)具有集合特征的數(shù)據(jù)結(jié)構(gòu)進(jìn)行遍歷通常可以使用next和hasNext方法,next實(shí)現(xiàn)取下一個(gè)用戶,hasNext判斷是否還有元素。因此我們定義一個(gè)接口Iterator,這個(gè)接口中定義兩個(gè)方法next和hasNext:
public interface Iterator {
public boolean hasNext(){}
public Object next(){}
}
而User類的listUses方法返回值改為Iterator接口的實(shí)現(xiàn)類:
public class User {
...public Iterator listUsers(){
}
...}
這樣就把User類的設(shè)計(jì)和具體的實(shí)現(xiàn)方法分離開了,因?yàn)榇藭r(shí)任何實(shí)現(xiàn)了next()和hasNext()方法的類都可以做為listUsers的返回值,都可以被用來表達(dá)“用戶列表”,而不僅僅可以使用Vector而已。比如,我可以用ArrayList來表達(dá)用戶列表,因?yàn)锳rrayList也實(shí)現(xiàn)了Iterator,當(dāng)然我也可以自己專門寫一個(gè)類來存放用戶列表,只要實(shí)現(xiàn)next()和hasNext()方法就行了。
這樣在具體的編寫代碼的時(shí)候,程序員具有了最大的靈活性,可以根據(jù)具體的情況,采用不同的編程方法來存放用戶列表。特別是降低了程序的耦合度,提高了程序的可移植性。對(duì)于上面那個(gè)JunkMail的listAllMail()方法也同樣應(yīng)該改為接口類型。
然后,在主程序里面就這樣來使用User類的listUsers方法:
User myUser = new User();
Iterator iterator = myUser.listUsers();
while(iterator.hasNext()){
iterator.next();
}
這樣就可以完全不用考慮程序代碼實(shí)現(xiàn)了,從高層次上把功能抽象出來,定義成為接口,同時(shí)又可以把系統(tǒng)設(shè)計(jì)的很合理,完全根據(jù)業(yè)務(wù)的需求來進(jìn)行設(shè)計(jì)。
結(jié)語
通過上面的幾個(gè)例子的設(shè)計(jì)說明,使用面向?qū)ο蟮乃季S方法,其實(shí)是一個(gè)把業(yè)務(wù)邏輯從具體的編程技術(shù)當(dāng)中抽象出來的過程,而這個(gè)抽象的過程是自上而下的,非常符合人類的思維習(xí)慣,也就是先不考慮問題解決的細(xì)節(jié),把問題的最主要的方面抽象成為一個(gè)簡(jiǎn)單的框架,集中精力思考如何解決主要矛盾,然后在解決問題的過程中,再把問題的細(xì)節(jié)分割成一個(gè)一個(gè)小問題,再專門去解決細(xì)節(jié)問題。
因而一旦牢牢的抓住了這一點(diǎn),你就會(huì)發(fā)現(xiàn)在軟件設(shè)計(jì)和開發(fā)過程中,你自己總是會(huì)不知不覺的運(yùn)用面向?qū)ο蟮乃季S方法來設(shè)計(jì)和編寫程序,并且程序的設(shè)計(jì)和開發(fā)也變得不再那么枯燥,而一個(gè)合理運(yùn)用面向?qū)ο蠹夹g(shù)進(jìn)行設(shè)計(jì)和架構(gòu)的軟件,更是具備了思維的藝術(shù)美感。
最后,愿面向?qū)ο蟮乃季S方法也能給您的程序設(shè)計(jì)之路帶來創(chuàng)作的樂趣。
第四篇:C#學(xué)習(xí)心得
集合聲明:類B可以換成任意object對(duì)象
1、CollectionBase
類A繼承CollectionBase類,通過CollectionBase的成員List實(shí)現(xiàn)類A的Add(類
B)、Remove(類B)和RemoveAt(類B)方法:
publicvoidAdd(類B newB)
{List.Add(newB);}
publicvoidRemove(類B newB)
{List.Remove(newB);}
publicvoidRemoveAt(int index)
{List.RemoveAt(index);}
在類A中建立索引可以按類似數(shù)組的方法訪問。
public 類B this[int index]
{get{return(類B)List[index];}
set{List[index]=value;}
}
利用CollectionBase的成員InnerList(ArrayList對(duì)象)實(shí)現(xiàn)類A的Contains()方法:
publicboolContains(類B newB)
{
returnInnerList.Contains(newB);
}
注意:InnerList是ArrayList類實(shí)例,其Contains方法通過調(diào)用Object.Equals確定相等性,Equals默認(rèn)實(shí)現(xiàn)僅支持引用相等。對(duì)于引用類型,相等定義為對(duì)象相等,即這些引用是否引用同一對(duì)象。對(duì)于值類型,相等定義為按位相等。
可以在類B中重寫Object.Equals方法和GetHashCode()方法。publicoverrideboolEquals(objectobj)
{//Check for null and compare run-time types.if(obj == null || GetType()!= obj.GetType())returnfalse;
B b =(B)obj;
return(比較邏輯);
}
publicoverrideintGetHashCode(){??}
2、DictionaryBase
類A繼承DictionaryBase類,通過DictionaryBase的成員
Dictionary(IDictionary類型的接口),實(shí)現(xiàn)類A的 Add(object key,類B)和Remove(object key,類B)方法:
publicvoidAdd(object key,類B newB)
{Dictionary.Add(key,newB);}
publicvoidRemove(object key,類B newB)
{Dictionary.Remove(key,newB);}
在類A中建立索引可以按類似數(shù)組的方法訪問。
public 類B this[object index]
{get{return(類B)Dictionary[index];}
set{Dictionary[index]=value;}
}
利用DictionaryBase的接口成員Dictionary實(shí)現(xiàn)類A的Contains()方法: publicboolContains(object key)
{
returnDictionary.Contains(key);
}
3、迭代器
對(duì)于繼承CollectionBase類的A,使用
foreach(BsourceBin類A對(duì)象){}
對(duì)于繼承DictionaryBase類的A,使用
foreach(DictionaryEntrysourceBin類A對(duì)象){source.Value.}
對(duì)于類迭代,使用方法GetEnumerator(),返回類型是IEnumerator;類成員迭代使用IEnumerable(),返回類型是IEnumerable;
例如繼承DictionaryBase類的A的迭代器,public new IEnumeratorGetEnumerator()
{foreach(object b in Dictionary.Values)
yield return(B)b;
}
以后使用foreach循環(huán)時(shí),可按照類似繼承CollectionBase類的的方式使用。
4、淺度復(fù)制與深度復(fù)制
淺度復(fù)制:簡(jiǎn)單地按照成員復(fù)制對(duì)象可以通過派生于System.Object的MemberwiseClone()方法來完成,這是一個(gè)受保護(hù)的方法,但是很容易在對(duì)象上定義一個(gè)調(diào)用該方法的公共方法例如GetCopy()。這個(gè)方法的復(fù)制功能成為淺復(fù)制。淺拷貝是對(duì)引用類型拷貝地址,對(duì)值類型直接進(jìn)行拷貝,但是string類例外,因?yàn)閟tring是readonly的,當(dāng)改變string類型的數(shù)據(jù)值時(shí),將重新分配了內(nèi)存地址。數(shù)組、類也是淺度復(fù)制,而結(jié)構(gòu)體、數(shù)值型、枚舉是深度復(fù)制。
深度復(fù)制:需要深度復(fù)制的類A添加ICloneable接口,實(shí)現(xiàn)該接口的Clone()方法。
public object Clone()
{A newA=new A();
object []arr=new object[維度];//object 可以是數(shù)值類型,string //不能使用newA.arr=arr;因?yàn)橥ㄟ^數(shù)組名賦值引用同一地址,是淺度復(fù)制 arr.CopyTo(newA.arr,0);
returnnewA;}
假設(shè)類A中有成員對(duì)象類B實(shí)例myB,則在類B定義中也要實(shí)現(xiàn)ICloneable的Clone()方法,class B:ICloneable
{
public object Clone(){??}
}
然后在類A的Clone方法中,newA.myB=myB.Clone();
比較
1、is運(yùn)算符
檢查對(duì)象是否是給定類型或者是否可以轉(zhuǎn)換為給定類型,是則返回true。
如果type是類類型,operand也是該類型,或繼承該類型、封箱到該類型,為true 如果type是接口類型,operand也是該類型,或?qū)崿F(xiàn)該接口的類型,為true 如果type是值類型,operand也是該類型,或拆箱到該類型,為true2、運(yùn)算符重載
public static 返回類型 operator 需重載的運(yùn)算符(參數(shù)??){}
注意不能重載賦值運(yùn)算符,&&和||運(yùn)算符,但可重載&和|;有些運(yùn)算符需成對(duì)重載,如“<”和“>”
3、IComparable接口
類A實(shí)現(xiàn)IComparable接口的方法intCompareTo(objectobj)后,利用成員為類A的實(shí)例的ArrayList或Array類可以調(diào)用Sort()方法,按CompareTo(objectobj)的方法排序。
4、IComparer接口
類A實(shí)現(xiàn)IComparer接口的方法intCompare(objectx, objecty)后,利用ArrayList或Array類可以調(diào)用Sort(IA)方法(IComparer IA=new A()),按
Compare(,)方法排序。注意ArrayList或Array類的實(shí)例不一定是類A。也可以在類A中定義一個(gè)公用動(dòng)態(tài)接口成員IComparer ID,這樣可以直接調(diào)用Sort(ID)。另外,在Compare方法中可以調(diào)用Comparer.Default.Compare(,)方法,實(shí)現(xiàn)特定的關(guān)鍵字排序。Default是Compare類的動(dòng)態(tài)實(shí)例。
轉(zhuǎn)換
1、隱式和顯示轉(zhuǎn)換
在沒有繼承關(guān)系,沒有共享接口的類型之間轉(zhuǎn)換時(shí),必須定義類型之間的隱式和顯示轉(zhuǎn)換。public classA
{??
//定義A到B的隱式轉(zhuǎn)換
public staticimplicit operatorzhuanB(Aa){?? return }
}
public classB
{??
//定義B到A的顯式轉(zhuǎn)換
public staticexplicit operatorzhuanA(Bb){??return }
}
2、as運(yùn)算符
把類型轉(zhuǎn)換為給定類型。
operand類型是type類型,或可以隱式轉(zhuǎn)換為type類型,或封箱到type類型 如果不能轉(zhuǎn)換,則表達(dá)式的結(jié)果是null
異常處理
Exception:所有異常對(duì)象的基類。
SystemException:運(yùn)行時(shí)產(chǎn)生的所有錯(cuò)誤的基類。
IndexOutOfRangeException:當(dāng)一個(gè)數(shù)組的下標(biāo)超出范圍時(shí)運(yùn)行時(shí)引發(fā)。NullReferenceException:當(dāng)一個(gè)空對(duì)象被引用時(shí)運(yùn)行時(shí)引發(fā)。
InvalidOperationException:當(dāng)對(duì)方法的調(diào)用對(duì)對(duì)象的當(dāng)前狀態(tài)無效時(shí),由某些方法引發(fā)。
ArgumentException:所有參數(shù)異常的基類。
ArgumentNullException:在參數(shù)為空(不允許)的情況下,由方法引發(fā)。ArgumentOutOfRangeException:當(dāng)參數(shù)不在一個(gè)給定范圍之內(nèi)時(shí),由方法引發(fā)。
InteropException:目標(biāo)在或發(fā)生在CLR外面環(huán)境中的異常的基類。ComException:包含COM類的HRESULT信息的異常。
SEHException:封裝Win32結(jié)構(gòu)異常處理信息的異常。
SqlException:封裝了SQL操作異常。
常見具體的異常對(duì)象:
ArgumentNullException一個(gè)空參數(shù)傳遞給方法,該方法不能接受該參數(shù)ArgumentOutOfRangeException參數(shù)值超出范圍
ArithmeticException出現(xiàn)算術(shù)上溢或者下溢
ArrayTypeMismatchException試圖在數(shù)組中存儲(chǔ)錯(cuò)誤類型的對(duì)象
BadImageFormatException圖形的格式錯(cuò)誤
DivideByZeroException除零異常
DllNotFoundException找不到引用的DLL
FormatException參數(shù)格式錯(cuò)誤
IndexOutOfRangeException數(shù)組索引超出范圍
InvalidCastException使用無效的類
InvalidOperationException方法的調(diào)用時(shí)間錯(cuò)誤
NotSupportedException調(diào)用的方法在類中沒有實(shí)現(xiàn)
NullReferenceException試圖使用一個(gè)未分配的引用OutOfMemoryException內(nèi)存空間不夠
StackOverflowException堆棧溢出
第五篇:C#學(xué)習(xí)心得
C#速成
一、緒論
C#是這樣的一種語言,具有C++的特點(diǎn),象Java一樣的編程風(fēng)格, 并且象Basic一樣的快速開發(fā)模型。如果你已經(jīng)知道了C++,本文會(huì)在不到一個(gè)小時(shí)的時(shí)間內(nèi)讓你迅速掌握C#的語法。熟悉Java的括會(huì)更好,因?yàn)镴ava的程序結(jié)構(gòu)、打包(Packages)和垃圾收集的概念有助于你更快的了解C#。因此在討論C#的構(gòu)造時(shí),我會(huì)假定你了解C++。
本文會(huì)討論C#語言的構(gòu)造與特點(diǎn),同時(shí)會(huì)采取簡(jiǎn)潔的和你能理解的方式使用些代碼示例,我們會(huì)盡量讓你能稍微看看這些代碼就能理解這些概念。
注意:本文不是為C#高手(C# gurus)所寫.這是針對(duì)在C#學(xué)習(xí)上還是初學(xué)者的文章。下面是將要討論的C#問題的目錄: 程序結(jié)構(gòu) 命名空間 數(shù)據(jù)類型 變量
運(yùn)算符和表達(dá)式 枚舉
語句(Statements)
類(Classes)和結(jié)構(gòu)(Structs)修飾符(Modifiers)屬性(Properties)接口(Interfaces)方法參數(shù)(Function Parameters)數(shù)組(Arrays)索引器(Indexers)裝箱及拆箱操作 委托(Delegates)繼承和多態(tài)
下面的內(nèi)容將不會(huì)在被討論之列:
C++與C#誰更通用
諸如垃圾回收、線程以及文件處理等概念 數(shù)據(jù)的類型轉(zhuǎn)換 異常處理.NET庫
二、程序結(jié)構(gòu)
這一點(diǎn)象C++,C#是一種對(duì)大小寫字母敏感的語言,分號(hào)“;”是語句間的分隔符。與C++不同的是,C#當(dāng)中聲明代碼文件(頭文件)與實(shí)現(xiàn)代碼文件(cpp文件)不是獨(dú)立存在的,所有代碼(類聲明和類實(shí)現(xiàn))都位于一個(gè)擴(kuò)展名為cs的文件內(nèi)。
讓我們瞧瞧C#當(dāng)中的 Hello world 程序是怎樣的。using System;namespace MyNameSpace { class HelloWorld { static void Main(string[] args){ Console.WriteLine(“Hello World”);} } }
在C#當(dāng)中的每樣?xùn)|西都被封裝到一個(gè)類中,C#的類又被封裝到一個(gè)命名空間當(dāng)中(就象一個(gè)文件夾中的文件)。類似于 C++,main方法是你的程序的入口點(diǎn)。C++的main函數(shù)調(diào)用名稱是“main”,而C#的main函數(shù)是以大寫字母M為起點(diǎn)的名稱是“Main”。
沒有必要把分號(hào)分隔符放在類語句塊或者結(jié)構(gòu)定義語句塊后。這在C++當(dāng)中被要求,但在C#當(dāng)中卻不是。
三、命名空間
每一個(gè)類都被包裝進(jìn)一個(gè)命名空間。命名空間的概念與C++的完全相同,但在C#當(dāng)中使用命名空間的頻率較C++還高。你可以使用點(diǎn)限定符(dot qulifier)訪問一個(gè)類。在上面的hello world程序當(dāng)中MyNameSpace就是一個(gè)命名空間。
現(xiàn)在思考這樣的一個(gè)問題,你想從某些別的類的命名空間當(dāng)中來訪問HelloWorld這個(gè)類該如何操作。
這有一個(gè)例子:
using System;namespace AnotherNameSpace { class AnotherClass { public void Func(){ Console.WriteLine(“Hello World”);} } }
現(xiàn)在,從你的HelloWorld類里你能象這樣去訪問上面的這個(gè)AnotherNameSpace的命名空間: using System;using AnotherNameSpace;// you will add this using statement namespace MyNameSpace { class HelloWorld { static void Main(string[] args){ AnotherClass obj = new AnotherClass();obj.Func();} } }
在.NET庫當(dāng)中,System是位于頂層的命名空間,別的命名空間都存在這個(gè)命名空間之下。默認(rèn)狀態(tài)下,存在一個(gè)全局的命名空間,因此一個(gè)在命名空間外定義的類將直接在這個(gè)全局命名空間之下;因此,你能在沒有任何點(diǎn)限定符的情況下訪問這個(gè)類。
四、變量
除以下區(qū)別外,C#當(dāng)中的變量幾乎與C++同:
與C++不同,C#變量被訪問之前必須被初始化;否則編譯時(shí)會(huì)報(bào)錯(cuò)。因此,訪問一個(gè)未初始化變量是不可能的事。
C#中你不會(huì)訪問到一個(gè)不確定的指針。(譯者注:嚴(yán)格說起來C#已經(jīng)把指針概念異化,限制更嚴(yán)格。所以有些資料上會(huì)說C#取消了指針概念)一個(gè)超出數(shù)組邊界的表達(dá)式是不可訪問的。
C#中沒有全局(整個(gè)Application)的變量或全局函數(shù),全局方式的操作是通過靜態(tài)函數(shù)和靜態(tài)變量來實(shí)現(xiàn)的。
五、數(shù)據(jù)類型
所有C#數(shù)據(jù)類型都派生自基類Object。這里有兩類數(shù)據(jù)類型: 基本型/內(nèi)置型 用戶自定義型 下面一個(gè)C#內(nèi)置類型列表:
類型 字節(jié)數(shù) 解釋 byte 1 無符號(hào)字節(jié)型 sbyte 1 有符號(hào)字節(jié)型 short 2 有符號(hào)短字節(jié)型 ushort 2 無符號(hào)短字節(jié)型 int 4 有符號(hào)整型 uint 4 無符號(hào)整型 long 8 有符號(hào)長整型 ulong 8 無符號(hào)長整型 float 4 浮點(diǎn)數(shù) double 8 雙精度數(shù) decimal 8 固定精度數(shù) string unicode字串型 char unicode字符型 bool 真假布爾型
注意:C#當(dāng)中的類型范圍與C++有所不同;例如,C++的long型是4個(gè)字節(jié),而在C#當(dāng)中是8個(gè)字節(jié)。同樣地,bool型和string型都不同于C++。bool型只接受true和false兩種值。不接受任何整數(shù)類型。
用戶定義類型包括: 類類型(class)結(jié)構(gòu)類型(struct)接口類型(interface)
數(shù)據(jù)類型的內(nèi)存分配形式的不同又把它們分成了兩種類型: 值類型(Value Types)
引用類型(Reference Types)
值類型:
值類型數(shù)據(jù)在棧中分配。他們包括:所有基本或內(nèi)置類型(不包括string類型)、結(jié)構(gòu)類型、枚舉類型(enum type)
引用類型:
引用類型在堆中分配,當(dāng)它們不再被使用時(shí)將被垃圾收集。它們使用new運(yùn)算符來創(chuàng)建,對(duì)這些類型而言,不存在C++當(dāng)中的delete操作符,根本不同于C++會(huì)顯式使用delete這個(gè)運(yùn)算符去釋放創(chuàng)建的這個(gè)類型。C#中,通過垃圾收集器,這些類型會(huì)自動(dòng)被收集處理。引用類型包括:類類型、接口類型、象數(shù)組這樣的集合類型類型、字串類型、枚舉類型 枚舉類型與C++當(dāng)中的概念非常相似。它們都通過一個(gè)enum關(guān)鍵字來定義。示例:
enum Weekdays { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday }
類類型與結(jié)構(gòu)類型的比較
除了在內(nèi)存分配形式上外,類與結(jié)構(gòu)的概念完全與C++相同。類的對(duì)象被分配在堆中,并且通過new來創(chuàng)建,結(jié)構(gòu)也是被new創(chuàng)建但卻被分配在棧當(dāng)中。C#當(dāng)中,結(jié)構(gòu)型適于快速訪問和擁有少量成員的數(shù)據(jù)類型。如果涉及量較多,你應(yīng)該創(chuàng)建一個(gè)類來實(shí)現(xiàn)他。
(譯者注:這與堆和棧內(nèi)存分配結(jié)構(gòu)的特點(diǎn)有關(guān)。簡(jiǎn)而言之,棧是一種順序分配的內(nèi)存;堆是不一定是連續(xù)的內(nèi)存空間。具體內(nèi)容需要大家參閱相關(guān)資料)示例:
struct Date { int day;int month;int year;}
class Date { int day;int month;int year;string weekday;string monthName;
public int GetDay(){ return day;}
public int GetMonth(){ return month;}
public int GetYear(){ return year;}
public void SetDay(int Day){ day = Day;}
public void SetMonth(int Month){ month = Month;}
public void SetYear(int Year){ year = Year;}
public bool IsLeapYear(){ return(year/4 == 0);}
public void SetDate(int day, int month, int year){
}...}
六、屬性
如果你熟悉C++面象對(duì)象的方式,你就一定有一個(gè)屬性的概念。在上面示例當(dāng)中,以C++的觀點(diǎn)來看,Data類的屬性就是day、month和year。用C#方式,你可以把它們寫成Get和Set方法。C#提供了一個(gè)更方便、簡(jiǎn)單、直接的方式來訪問屬性。
因此上面的類可以被寫成:
using System;class Date { int day;public int Day{ get { return day;}
set { day = value;} }
int month;
public int Month{
get { return month;}
set { month = value;} }
int year;
public int Year{
get { return year;}
set { year = value;} }
public bool IsLeapYear(int year){ return year%4== 0 ? true: false;}
public void SetDate(int day, int month, int year){ this.day = day;this.month = month;this.year = year;} }
你可在這里得到并設(shè)置這些屬性:
class User {
public static void Main(){ Date date = new Date();date.Day = 27;date.Month = 6;date.Year = 2003;Console.WriteLine(“Date: {0}/{1}/{2}”, date.Day, date.Month, date.Year);} }
七、修飾符
你必須已經(jīng)知道public、private、protected這些常在C++當(dāng)中使用的修飾符。這里我會(huì)討論一些C#引入的新的修飾符。readonly(只讀)
readonly修飾符僅在類的數(shù)據(jù)成員中使用。正如這名字所提示的,readonly 數(shù)據(jù)成員僅能只讀,它們只能在構(gòu)造函數(shù)或是直接初始化操作下賦值一次。readonly與const數(shù)據(jù)成員不同,const 要求你在聲明中初始化,這是直接進(jìn)行的。看下面的示例代碼: class MyClass {
const int constInt = 100;//直接初始化
readonly int myInt = 5;//直接初始化
readonly int myInt2;//譯者注:僅做聲明,未做初始化
public MyClass(){ myInt2 = 8;//間接的 }
public Func(){ myInt = 7;//非法操作(譯者注:不得賦值兩次)Console.WriteLine(myInt2.ToString());} }
sealed(密封)
密封類不允許任何類繼承,它沒有派生類。因此,你可以對(duì)你不想被繼承的類使用sealed關(guān)鍵字。
sealed class CanNotbeTheParent { int a = 5;}
unsafe(不安全)
你可使用unsafe修飾符來定義一個(gè)不安全的上下文。在不安全的上下文里,你能寫些如C++指針這樣的不安全的代碼。看下面的示例代碼:
public unsafe MyFunction(int * pInt, double* pDouble){ int* pAnotherInt = new int;*pAnotherInt = 10;pInt = pAnotherInt;...*pDouble = 8.9;}
八、interface(接口)
如果你有COM方面的概念,你會(huì)立亥明白我要談?wù)摰膬?nèi)容。一個(gè)接口就是一個(gè)抽象的基類,這個(gè)基類僅僅包含功能描述,而這些功能的實(shí)現(xiàn)則由子類來完成。C#中你要用interface關(guān)鍵字來定義象接口這樣的類。.NET就是基于這樣的接口上的。C#中你不支持C++所允許的類多繼承(譯者注:即一個(gè)派生類可以從兩個(gè)或兩個(gè)以上的父類中派生)。但是多繼承方式可以通過接口獲得。也就是說你的一個(gè)子類可以從多個(gè)接口中派生實(shí)現(xiàn)。interface myDrawing { int originx { get;set;} int originy { get;set;} void Draw(object shape);} class Shape: myDrawing { int OriX;int OriY;public int originx { get{ return OriX;} set{ OriX = value;} } public int originy { get{ return OriY;} set{ OriY = value;} } public void Draw(object shape){...// do something }
// class's own method public void MoveShape(int newX, int newY){.....} }
九、Arrays(數(shù)組)
C#中的數(shù)組比C++的表現(xiàn)更好。數(shù)組被分配在堆中,因此是引用類型。你不可能訪問超出一個(gè)數(shù)組邊界的元素。因此,C#會(huì)防止這樣類型的bug。一些輔助方式可以循環(huán)依次訪問數(shù)組元素的功能也被提供了,foreach就是這樣的一個(gè)語句。與C++相比,C#在數(shù)組語法上的特點(diǎn)如下: 方括號(hào)被置于數(shù)據(jù)類型之后而不是在變量名之后。創(chuàng)建數(shù)組元素要使用new操作符。
C#支持一維、多維以及交錯(cuò)數(shù)組(數(shù)組中的數(shù)組)。示例:
int[] array = new int[10];// 整型一維數(shù)組 for(int i = 0;i < array.Length;i++){ array[i] = i;} int[,] array2 = new int[5,10];// 整型二維數(shù)組 array2[1,2] = 5;int[,] array3 = new int[5,10,5];// 整型的三維數(shù)組 array3[0,2,4] = 9;int[][] arrayOfarray = = new int[2];// 整型交錯(cuò)數(shù)組(數(shù)組中的數(shù)組)arrayOfarray[0] = new int[4];arrayOfarray[0] = new int[] {1,2,15};
十、索引器
索引器被用于寫一個(gè)訪問集合元素的方法,集合使用“[]”這樣的直接方式,類似于數(shù)組。你所要做的就是列出訪問實(shí)例或元素的索引清單。類的屬性帶的是輸入?yún)?shù),而索引器帶的是元素的索引表,除此而外,他們二者的語法相同。示例: 注意:CollectionBase是一個(gè)制作集合的庫類。List是一個(gè)protected型的CollectionBase成員,儲(chǔ)存著集合清單列表。class Shapes: CollectionBase { public void add(Shape shp){ List.Add(shp);} //indexer public Shape this[int index] { get { return(Shape)List[index];} set { List[index] = value;} } }
十一、裝箱和拆箱操作(Boxing/Unboxing)
C#的裝箱思想是全新的。上面提到過所有的數(shù)據(jù)類型,不論內(nèi)置或用戶自定義,全都從命名空間System的一個(gè)基類object派生出來。因此把基本的或者原始類型轉(zhuǎn)換成object類型被稱做裝箱,反之,這種方式的逆操作被稱為拆箱。示例: class Test { static void Main(){ int myInt = 12;object obj = myInt;// 裝箱 int myInt2 =(int)obj;// 拆箱 } }
示例展示了裝箱和拆箱操作。一個(gè)整型值轉(zhuǎn)換成object類型,然后又轉(zhuǎn)換回整型。當(dāng)一個(gè)值類型的變量需要轉(zhuǎn)換成引用類型時(shí),一個(gè)object的箱子會(huì)被分配容納這個(gè)值的空間,這個(gè)值會(huì)被復(fù)制進(jìn)這個(gè)箱子。拆箱與此相反,一個(gè)object箱子中的數(shù)據(jù)被轉(zhuǎn)換成它的原始值類型時(shí),這個(gè)值將被從箱中復(fù)制到適當(dāng)?shù)拇鎯?chǔ)位置。
十二、方法參數(shù)
C#中有三種類型的參數(shù):
值參數(shù)/輸入型參數(shù)
引用型參數(shù)/輸入輸出型參數(shù) Out參數(shù)
如果你有COM接口和它的參數(shù)類型的概念,你會(huì)很容易理解C#參數(shù)類型。值參數(shù)/輸入型參數(shù)
值概念與C++相同。所要傳遞的值會(huì)被復(fù)制到一個(gè)位置上并被傳遞給函數(shù)。示例: SetDay(5);void SetDay(int day){....}
引用型參數(shù)/輸入輸出參數(shù)
C#中的引用參數(shù)既不是C++中的指針也不是引用操作符(&)來傳遞的。C#的引用型參數(shù)減少了出錯(cuò)的可能。引用型參數(shù)也被稱作輸入輸出參數(shù),因?yàn)槟銈鬟f了一個(gè)引用地址,因此你可以從函數(shù)中傳遞一個(gè)輸入值并且可以獲得一個(gè)輸出值。
你不能把一個(gè)未經(jīng)初始化的引用型參數(shù)傳遞給函數(shù)。C#用ref這個(gè)關(guān)鍵字來聲明引用型參數(shù)。當(dāng)你傳遞一個(gè)變量給函數(shù)要求的引用參數(shù)時(shí)必須使用一個(gè)ref關(guān)鍵字說明。示例:
int a= 5;FunctionA(ref a);// 要用ref聲明變量,否則你會(huì)得到 // 一個(gè)編譯錯(cuò)誤
Console.WriteLine(a);// 指向地址的值為20 void FunctionA(ref int Val){ int x= Val;Val = x* 4;}
Out參數(shù)
Out型參數(shù)僅僅從函數(shù)當(dāng)中返回一個(gè)值。不要求有輸入值。C#用關(guān)鍵字out來描聲明這個(gè)參數(shù) 示例:
int Val;GetNodeValue(Val);bool GetNodeValue(out int Val){ Val = value;return true;}
可變數(shù)量的參數(shù)和數(shù)組
數(shù)組在C#當(dāng)中是通過關(guān)鍵字params來描述傳遞的。作為數(shù)組類型的變量,你能傳遞任意數(shù)量的元素。從下面示例中你可以理解的更好。示例:
void Func(params int[] array){ Console.WriteLine(“number of elements {0}”,array.Length);} Func();// prints 0 Func(5);// prints 1 Func(7,9);// prints 2 Func(new int[] {3,8,10});// prints 3 int[] array = new int[8] {1,3,4,5,5,6,7,5};Func(array);// prints 8
十三、運(yùn)算符和表達(dá)式
運(yùn)算符和表達(dá)式概念與C++完全相同。但是一些新的有用的運(yùn)算符被填加了進(jìn)來。我將在這里討論其中的某些部分。
is 運(yùn)算符
is 運(yùn)算符被用于檢查操作數(shù)的類型是否相同或者是否可以轉(zhuǎn)換。is 運(yùn)算符在多態(tài)環(huán)境下特別有用。它有兩個(gè)操作數(shù),運(yùn)算結(jié)果是一個(gè)布爾型。看這個(gè)示例: void function(object param){ if(param is ClassA)//do something else if(param is MyStruct)//do something } } as 運(yùn)算符
as 運(yùn)算符檢查操作數(shù)的類型是否可被轉(zhuǎn)換或者是否相等(這些 as通過 is 運(yùn)算符來完成。如果結(jié)果是可轉(zhuǎn)換的,則結(jié)果將被轉(zhuǎn)換或者被裝箱,成object(關(guān)于as運(yùn)算符進(jìn)行裝箱成目標(biāo)類型的操作請(qǐng)看前面的裝箱/拆箱操作)。如果不可轉(zhuǎn)換或者裝箱,則返回值是null。瞧一瞧下面的例子我們會(huì)更好地理解這個(gè)概念。Shape shp = new Shape();Vehicle veh = shp as Vehicle;// 結(jié)果是null, 類型不可轉(zhuǎn)換 Circle cir = new Circle();Shape shp = cir;Circle cir2 = shp as Circle;//會(huì)被轉(zhuǎn)換 object[] objects = new object[2];objects[0] = “Aisha”;object[1] = new Shape();string str;for(int i=0;i&< objects.Length;i++){ str = objects[i] as string;if(str == null)Console.WriteLine(“can not be converted”);else Console.WriteLine(“{0}”,str);} 輸出: Aisha can not be converted
十四、語句
除了對(duì)某些新增語句和對(duì)某些語句的修改以外,C#語句與C++非常相象。下面是新增的語句: foreach 用于循環(huán)依次訪問集合元素,比如象數(shù)組等。示例: foreach(string s in array)Console.WriteLine(s);lock 用于鎖住代碼塊,使線程在臨界爭(zhēng)區(qū)內(nèi),別的線程無法進(jìn)入鎖定的臨界區(qū)。
checked/unchecked 用于數(shù)值運(yùn)算中的溢出檢測(cè)。示例:
int x = Int32.MaxValue;x++;// 溢出檢測(cè) { x++;// 異常
}
unchecked { x++;// 溢出} }
下面的語句在C#當(dāng)中已經(jīng)被修改: Switch 執(zhí)行一個(gè)case語句后,程序流程不允許跳到下一個(gè)相鄰case語句。這在C++當(dāng)中是被允許的。示例:
int var = 100;switch(var){ case 100: Console.WriteLine(“
case 200: Console.WriteLine(“
C++編譯后的輸出:
C#下,編譯時(shí)會(huì)報(bào)錯(cuò):
error CS0163: Control cannot fall through from one case label('case 100:')to another 但是你仍然能做C++類似的事 switch(var){ case 100: case 200: Console.WriteLine(“100 or 200
你也可以常數(shù)變量作為case 的值: 示例:
const string WeekEnd = “Sunday”;const string WeekDay1 = “Monday”;....string WeekDay = Console.ReadLine();switch(WeekDay){ case WeekEnd: Console.WriteLine(“It's weekend!”);break;case WeekDay1: Console.WriteLine(“It's Monday”);break;}
十五、委托
委托讓我們把一個(gè)函數(shù)引用存儲(chǔ)在一個(gè)變量里。C++當(dāng)中,這類似于使用typedef定義的函數(shù)指針,我們通常用存儲(chǔ)一個(gè)函數(shù)指針。
聲明委托使用的關(guān)鍵字是 delegate。瞧瞧這個(gè)示例,你會(huì)理解什么是委托: 示例:
delegate int Operation(int val1, int val2);public int Add(int val1, int val2){ return val1 + val2;}
public int Subtract(int val1, int val2){ return val1-val2;}
public void Perform(){ Operation Oper;Console.WriteLine(“Enter + or-”);string optor = Console.ReadLine();Console.WriteLine(“Enter 2 operands”);string opnd1 = Console.ReadLine();string opnd2 = Console.ReadLine();int val1 = Convert.ToInt32(opnd1);int val2 = Convert.ToInt32(opnd2);if(optor == “+”)Oper = new Operation(Add);Else Oper = new Operation(Subtract);Console.WriteLine(“ Result = {0}”, Oper(val1, val2));}
十六、繼承和多態(tài)
C#僅允許單繼承,多繼承要通過接口來實(shí)現(xiàn)。示例:
class Parent { } class Child : Parent { }
十七、虛擬方法
除了在子類中實(shí)現(xiàn)虛擬方法采用override關(guān)鍵字外,虛擬方法實(shí)現(xiàn)多態(tài)的概念C#與C++相同。父類使用相同的virtual關(guān)鍵字。從重載虛擬方法的每個(gè)類都要使用override關(guān)鍵字。class Shape { public virtual void Draw(){ Console.WriteLine(“Shape.Draw”);} }
class Rectangle : Shape { public override void Draw(){ Console.WriteLine(“Rectangle.Draw”);} }
class Square : Rectangle { public override void Draw(){ Console.WriteLine(“Square.Draw”);} }
class MainClass { static void Main(string[] args){ Shape[] shp = new Shape[3];Rectangle rect = new Rectangle();shp[0] = new Shape();shp[1] = rect;shp[2] = new Square();shp[0].Draw();shp[1].Draw();shp[2].Draw();} }
輸出t: Shape.Draw Rectangle.Draw Square.Draw
十八、使用“new”來隱藏父方法
你可以定義一個(gè)子類成一個(gè)新方法版本,隱藏基類當(dāng)中的那個(gè)版本。使用new關(guān)鍵字就可以定義一個(gè)新版本。思考下面的示例,它是上面示例的修改后的版本。注意當(dāng)我用Rectangle類中的new關(guān)鍵字代替override關(guān)鍵字時(shí)示例的輸出情況。
class Shape { public virtual void Draw(){ Console.WriteLine(“Shape.Draw”);} }
class Rectangle : Shape { public new void Draw(){ Console.WriteLine(“Rectangle.Draw”);} } class Square : Rectangle { //沒在這里讓你重載 public new void Draw(){ Console.WriteLine(“Square.Draw”);} } class MainClass { static void Main(string[] args){ Console.WriteLine(“Using Polymorphism:”);Shape[] shp = new Shape[3];Rectangle rect = new Rectangle();shp[0] = new Shape();shp[1] = rect;shp[2] = new Square();shp[0].Draw();shp[1].Draw();shp[2].Draw();Console.WriteLine(“Using without Polymorphism:”);rect.Draw();Square sqr = new Square();sqr.Draw();} } 輸出: Using Polymorphism Shape.Draw Shape.Draw Shape.Draw Using without Polymorphism: Rectangle.Draw Square.Draw 這里的多態(tài)性不會(huì)把Rectangle類的Draw方法當(dāng)做Shape的Draw方法多態(tài)性的一種表現(xiàn)。相反,它會(huì)認(rèn)為這是一種不同的方法。因此,為了避免父類與子類間的命名沖突,我們使用了new修飾符。注意:你不能使用同一類下面一種方法的兩個(gè)版本,即一個(gè)是用new修飾符的版本,另一個(gè)是用override或virtual修飾符的版本。正象上面示例所說明的,我不能再在擁有virtual或override方法的Rectangle類中添加另一個(gè)命名為Draw的方法。同樣地,在Square類中,我也不能重載Square類的虛擬的Draw方法。
十九、調(diào)用基類成員
如果子類與基類有同名的數(shù)據(jù)成員,為避免命名沖突,訪問基類數(shù)據(jù)成員和函要使用一個(gè)關(guān)鍵字base。在下面的示例中我們來看看如何調(diào)用基類的構(gòu)造函數(shù)以及如何使用數(shù)據(jù)成員。public Child(int val):base(val){ myVar = 5;base.myVar;}
或者
public Child(int val){ base(val);myVar = 5;base.myVar;}
文學(xué)の音聲図書館[轉(zhuǎn)貼]
みなさん: こんにちは。私がつくった「文學(xué)の音聲図書館」をご覧下さい。みなさまの聴力に役立つかもしれません。どうぞ。
http://作家辭典http://horagai.com/歌舞伎文庫http://http://日語教材 http://seki.nease.net/yufa.htm 語法網(wǎng)站======================================外來語從此不再可怕http://[align=right][color=#000066][此貼子已經(jīng)被作者于2005-1-27 20:18:53編輯過][/color][/align]