第三章C面向?qū)ο蠡A_第1頁
第三章C面向?qū)ο蠡A_第2頁
第三章C面向?qū)ο蠡A_第3頁
第三章C面向?qū)ο蠡A_第4頁
第三章C面向?qū)ο蠡A_第5頁
已閱讀5頁,還剩73頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

第三章C面向?qū)ο蠡A面向?qū)ο蟮娜齻€特征:1.封裝所謂“封裝”,就是用一個框架把數(shù)據(jù)和代碼組合在一起,形成一個對象。在C#中,類是支持對象封裝的工具,對象則是封裝的基本單元。2.繼承繼承是父類和子類之間共享數(shù)據(jù)和方法的機制,通常把父類稱為基類,子類稱為派生類。如果一個類有兩個或兩個以上的直接基類,這樣的繼承結構被稱為多重繼承或多繼承。C#通過接口來實現(xiàn)多重繼承。接口可以從多個基接口繼承。3.多態(tài)性在面向?qū)ο缶幊讨校鄳B(tài)是指同一個操作作用于不同的對象,可以有不同的解釋,產(chǎn)生不同的執(zhí)行結果。多態(tài)性有兩種,一種是靜態(tài)多態(tài),一種是動態(tài)多態(tài)。3.2類類就是一種數(shù)據(jù)結構,它定義數(shù)據(jù)和操作這些數(shù)據(jù)的代碼。3.2.1類的聲明語法形式:[屬性集信息][類修飾符]class類名[:類基]{ [類主體]}其中:屬性集信息——是C#語言提供給程序員的,為程序中定義的各種實體附加一些說明信息,這是C#語言的一個重要特征。類修飾符——可以是表3.1所列的幾種之一或是它們的有效組合,但在類聲明中,同一修飾符不允許出現(xiàn)多次。表3.1類修飾符修飾符作用說明public表示不限制對類的訪問。類的訪問權限省略時默認為publicprotected表示該類只能被這個類的成員或派生類成員訪問private表示該類只能被這個類的成員訪問internal表示該類能夠由程序集中的所有文件使用,而不能由程序集之外的對象使用new只允許用在嵌套類中,它表示所修飾的類會隱藏繼承下來的同名成員abstract表示這是一個抽象類,該類含有抽象成員,因此不能被實例化,只能用作基類sealed表示這是一個密封類,不能從這個類再派生出其他類。顯然密封類不能同時為抽象類3.2.2類的成員

類的定義包括類頭和類體兩部分,其中類體用一對大花括號{}括起來,類體用于定義該類的成員。語法形式:{[類成員聲明]}類成員由兩部分組成,一個是類體中以類成員聲明形式引入的類成員,另一個則是直接從它的基類繼承而來的成員。類成員聲明主要包括:常數(shù)聲明、字段聲明、方法聲明、屬性聲明、事件聲明、索引器聲明、運算符聲明、構造函數(shù)聲明、析構函數(shù)聲明、靜態(tài)構造函數(shù)、類型聲明等。1.常數(shù)聲明

語法形式:[屬性集信息][常數(shù)修飾符]const類型標識符=常數(shù)表達式[,…]其中:常數(shù)修飾符——new、public、protected、internal、private。類型——sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool、string、枚舉類型或引用類型。常數(shù)表達式的值類型應與目標類型一致,或者通過隱式轉(zhuǎn)換規(guī)則轉(zhuǎn)換成目標類型。例如:classA_const{publicconstintX=10;constdoublePI=3.14159;//默認訪問修飾符,即約定為privateconstdoubleY=0.618+3.14;}常數(shù)聲明不允許使用static修飾符,但它和靜態(tài)成員一樣只能通過類訪問。例如:classTest{publicstaticvoidMain(){A_constm=newA_const();Console.WriteLine("X={0},PI={1},Y={2}",A_const.X,A_const.PIA_const.Y);}}2.字段聲明語法形式:[屬性集信息][字段修飾符]類型變量聲明列表;其中:變量聲明列表——標識符或用逗號“,”分隔的多個標識符,并且變量標識符還可用賦值號“=”設定初始值。例如:classA{intx=100,y=200;floatsum=1.0f;}

字段修飾符——new、public、protected、internal、private、static、readonly、volatile?!纠?.1】

通過構造函數(shù)給只讀字段賦值usingSystem;publicclassArea{ publicreadonlydoubleRadius;//Radius是只讀字段

privatedoublex,y; publicdoubleSize; publicstaticdoubleSum=0.0; publicArea() { Radius=1.0; //通過構造函數(shù)對radius賦值

}}classTest{ publicstaticvoidMain() { Areas1=newArea(); Console.WriteLine("Radius={0},Size={1},Sum={2}",s1.Radius,s1.Size,Area.Sum); //靜態(tài)字段通過類訪問Area.Sum,實例字段通過對象訪問s1.Size Console.Read(); }}

無論是靜態(tài)字段還是實例字段,它們的初始值都被設置成字段類型的默認值。如果字段聲明包含變量初始值設定,則在初始化執(zhí)行期間相當于執(zhí)行一個賦值語句。對靜態(tài)字段的初始化發(fā)生在第一次使用該類靜態(tài)字段之前,執(zhí)行的順序按靜態(tài)字段在類聲明中出現(xiàn)的文本順序進行。當實例字段的初始化發(fā)生在創(chuàng)建一個類的實例時,同樣是按實例字段在類聲明中的文本順序執(zhí)行的。常量與ReadOnly字段的區(qū)別都是只讀Readonly字段的賦值只能在聲明的同時或者構造函數(shù)實現(xiàn)。Const成員的值要求在編譯時就能計算。Const不允許使用Static修飾,只能通過類訪問。3.2.3構造函數(shù)和析構函數(shù)1.構造函數(shù)提供了實現(xiàn)對象進行初始化的方法,這就是構造函數(shù)。在C#中,類的成員字段可以分為實例字段和靜態(tài)字段,與此相應的構造函數(shù)也分為實例構造函數(shù)和靜態(tài)構造函數(shù)。(1)實例構造函數(shù)的聲明語法形式:[屬性集信息][構造函數(shù)修飾符]標識符([參數(shù)列表])[:base([參數(shù)列表])][:this([參數(shù)列表])]{構造函數(shù)語句塊}其中:構造函數(shù)修飾符——public、protected、internal、private、extern。一般地,構造函數(shù)總是public類型的。標識符([參數(shù)列表]opt)——構造函數(shù)名,必須與這個類同名,不聲明返回類型,并且沒有任何返回值。它與返回值類型為void的函數(shù)不同。構造函數(shù)重載。用new運算符創(chuàng)建一個類的對象時,類名后的一對圓括號提供初始化列表,這實際上就是提供給構造函數(shù)的參數(shù)。系統(tǒng)根據(jù)這個初始化列表的參數(shù)個數(shù)、參數(shù)類型和參數(shù)順序調(diào)用不同的構造函數(shù)。usingSystem;publicclassTime{ privateinthour,minute,second; publicTime() { hour=minute=second=0; } publicTime(inth) {hour=h; minute=second=0; } publicTime(inth,intm) { hour=h; minute=m; second=0; }

publicTime(inth,intm,ints) { hour=h; minute=m; second=s; }}classTest{ staticvoidMain()

{

Timet1,t2,t3,t4;//對t1,t2,t3,t4分別調(diào)用不同的構造函數(shù)

t1=newTime(); t2=newTime(8); t3=newTime(8,30); t4=newTime(8,30,30); }

}【例3.2】

Time類的構造函數(shù)及其重載?!纠?.3】

構造函數(shù)初始化。

實例對象創(chuàng)建時,根據(jù)不同的參數(shù)調(diào)用相應的構造函數(shù)完成初始化。usingSystem;classPoint{publicdoublex,y;publicPoint()

{

x=0;

y=0;

}publicPoint(doublex,doubley)

{

this.x=x;//當this在實例構造函數(shù)中使用時,

this.y=y;//它的值就是對該構造的對象的引用

}}classTest{ publicstaticvoidMain() { Pointa=newPoint(); Pointb=newPoint(3,4); //用構造函數(shù)初始化對象

Console.WriteLine("a.x={0},a.y={1}",a.x,a.y); //a.x=0,a.y=0 Console.WriteLine("b.x={0},b.y={1}",b.x,b.y); //b.x=3,b.y=4 Console.Read();

}}派生類構造函數(shù)的調(diào)用次序【例3.4】

派生類構造函數(shù)及其調(diào)用。usingSystem;classPoint{ privateintx,y; publicPoint() { x=0;

y=0; Console.WriteLine("Point()constructor:{0}",this); } publicPoint(intx,inty) {

this.x=x; this.y=y;

Console.WriteLine("Point(x,y)constructor:{0}",this); }}classCircle:Point{ privatedoubleradius; publicCircle() //默認約定調(diào)用基類的無參構造函數(shù)Point() {

Console.WriteLine("Circle()constructor:{0}",this); } publicCircle(doubleradius):base() { this.radius=radius; Console.WriteLine("Circle(radius)constructor:{0}",this); } publicCircle(intx,inty,doubleradius):base(x,y) { this.radius=radius; Console.WriteLine("Circle(x,y,radius)constructor:{0}",this); }}classTest{ staticvoidMain() { Pointa=newPoint(); Circleb=newCircle(3.5); Circlec=newCircle(1,1,4.8); Console.Read();

}}

程序運行結果如下:(2)靜態(tài)構造函數(shù)的聲明語法形式:[屬性集信息][靜態(tài)構造函數(shù)修飾符]標識符(){

靜態(tài)構造函數(shù)體}其中:靜態(tài)構造函數(shù)修飾符——[extern]static或者static[extern]。如果有extern修飾,則說明這是一個外部靜態(tài)構造函數(shù),不提供任何實際的實現(xiàn),所以靜態(tài)構造函數(shù)體僅僅是一個分號。標識符()——是靜態(tài)構造函數(shù)名,必須與這個類同名,靜態(tài)構造函數(shù)不能有參數(shù)。靜態(tài)構造函數(shù)體——靜態(tài)構造函數(shù)的目的是對靜態(tài)字段的初始化,所以它只能對靜態(tài)數(shù)據(jù)成員進行初始化,而不能對非靜態(tài)數(shù)據(jù)成員進行初始化。關于構造函數(shù),還有幾點請注意:

:base([參數(shù)列表]opt)——表示調(diào)用直接基類中的實例構造函數(shù)。

:this([參數(shù)列表]opt)——表示調(diào)用該類本身所聲明的其他構造函數(shù)。構造函數(shù)語句塊——既可以對靜態(tài)字段賦值,也可以對非靜態(tài)字段進行初始化。但在構造函數(shù)體中不要做對類的實例進行初始化以外的事情,也不要嘗試顯式地調(diào)用構造函數(shù)。實例構造函數(shù)——不能被繼承。如果一個類沒有聲明任何實例構造函數(shù),則系統(tǒng)會自動提供一個默認的實例構造函數(shù)?!纠?.5】

靜態(tài)構造函數(shù)。usingSystem;classScreen{ staticintHeight; staticintWidth; intCur_X,Cur_Y; staticScreen(){//靜態(tài)構造函數(shù),對類的靜態(tài)字段初始化

Height=768; Width=1024; }}關于靜態(tài)構造函數(shù),請注意:靜態(tài)構造函數(shù)是不可繼承的,而且不能被直接調(diào)用。只有創(chuàng)建類的實例或者引用類的任何靜態(tài)成員時,才能激活靜態(tài)構造函數(shù),所以在給定的應用程序域中靜態(tài)構造函數(shù)至多被執(zhí)行一次。如果類中沒有聲明靜態(tài)構造函數(shù),而又包含帶有初始設定的靜態(tài)字段,那么編譯器會自動生成一個默認的靜態(tài)構造函數(shù)。2.析構函數(shù)

語法形式:[屬性集信息][extern]~標識符(){

析構函數(shù)體}其中:標識符——必須與類名相同,但為了區(qū)分構造函數(shù),前面需加“~”析構函數(shù)——不能寫返回類型,也不能帶參數(shù),因此它不可能被重載,當然它也不能被繼承,所以一個類最多只能有一個析構函數(shù)。關于析構函數(shù),請注意:析構函數(shù)不能由程序顯式地調(diào)用,而是由系統(tǒng)在釋放對象時自動調(diào)用。如果對象是一個派生類對象,那么在調(diào)用析構函數(shù)時也會產(chǎn)生鏈式反應,首先執(zhí)行派生類的析構函數(shù),然后執(zhí)行基類的析構函數(shù),如果這個基類還有自己的基類,這個過程就會不斷重復,直到調(diào)用Object類的析構函數(shù)為止,其執(zhí)行順序正好與構造函數(shù)相反。3.3方法C#沒有全局常數(shù)、全局變量和全局方法,任何事物都必須封裝在類中。通常,程序的其他部分通過類所提供的方法與它進行互操作。對方法的理解可以從方法的聲明、方法的參數(shù)、靜態(tài)方法與實例方法、方法的重載與覆蓋等方面切入。3.3.1方法的聲明方法是按照一定格式組織的一段程序代碼,在類中用方法聲明的方式來定義。語法形式:[屬性集信息][方法修飾符]返回類型方法名([形參表]){

方法體}表3.2方法修飾符修飾符作用說明new在一個繼承結構中,用于隱藏基類同名的方法public表示該方法可以在任何地方被訪問protected表示該方法可以在它的類體或派生類類體中被訪問,但不能在類體外訪問private表示該方法只能在這個類體內(nèi)被訪問internal表示該方法可以被同處于一個工程的文件訪問static表示該方法屬于類型本身,而不屬于某特定對象virtual表示該方法可在派生類中重寫,來更改該方法的實現(xiàn)abstract表示該方法僅僅定義了方法名及執(zhí)行方式,但沒有給出具體實現(xiàn),所以包含這種方法的類是抽象類,有待于派生類的實現(xiàn)override表示該方法是將從基類繼承的virtual方法的新實現(xiàn)sealed表示這是一個密封方法,它必須同時包含override修飾,以防止它的派生類進一步重寫該方法extern表示該方法從外部實現(xiàn)方法修飾符中public、protected、private、internal、protectedinternal屬于訪問修飾符,表示訪問的級別,默認情況下,方法的訪問級別為public。訪問修飾符的組合表3.3所列的組合被視為非法的無效組合。返回類型——方法可以返回值也可以不返回值。如果返回值,則需要說明返回值的類型,默認情況下為void。表3.3修飾符的無效組合修飾符不能與下列選項一起使用staticvirtual、abstract和overridevirtualstatic、abstract和overrideoverridenew、static和virtualabstractvirtual和staticnewoverrideexternabstract方法名——每個方法都有一個名稱Main()是為開始執(zhí)行程序的方法預留的,不要使用C#的關鍵字作為方法名。方法的命名盡可能地顧名思義。形參表——由零個或多個用逗號分隔的形式參數(shù)組成,形式參數(shù)可用屬性、參數(shù)修飾符、類型等描述。當形參表為空時,外面的圓括號也不能省略。方法體——用花括號括起的一個語句塊。usingSystem;classStackTp{

intMaxSize;intTop;int[]StkList;publicStackTp()//構造函數(shù)

{ MaxSize=100; Top=0; StkList=newint[MaxSize];}publicStackTp(intsize)//構造函數(shù)

{ MaxSize=size; Top=0; StkList=newint[MaxSize];}

publicboolisEmptyStack()//方法{if(Top==0) returntrue;else returnfalse;}publicboolisFullStack(){if(Top==MaxSize) returntrue;elsereturnfalse;}publicvoidpush(intx){StkList[Top]=x;Top++;}}【例3.6】

StackTp類定義了幾個方法以模擬實現(xiàn)一個壓棧操作。classTest{publicstaticvoidMain()

{StackTpST=newStackTp(20); strings1; if(ST.isEmptyStack()) //調(diào)用方法isEmptyStack() s1="Empty"; else s1="notEmpty"; Console.WriteLine("Stackis"+s1); for(inti=0;i<20;i++) ST.push(i+1); if(ST.isFullStack()) //調(diào)用方法isFullStack() s1="Full"; else s1="notFull"; Console.WriteLine("Stackis"+s1); Console.Read();

}}程序運行結果如下:3.3.2方法的參數(shù)

參數(shù)的傳入或傳出就是在實參與形參之間發(fā)生的.在C#中實參與形參有4種傳遞方式。1.值參數(shù)在方法聲明時不加修飾的形參就是值參數(shù),它表明實參與形參之間按值傳遞。這種傳遞方式的好處是,在方法中對形參的修改不影響外部的實參,也就是說,數(shù)據(jù)只能傳入方法而不能從方法傳出,所以值參數(shù)有時也被稱為入?yún)?shù)。usingSystem;classMyclass{publicvoidSort(intx,inty,intz){ inttmp;//tmp是方法Sort的局部變量

//將x,y,z按從小到大排序

if(x>y){tmp=x;x=y;y=tmp;} if(x>z){tmp=x;x=z;z=tmp;} if(y>z){tmp=y;y=z;z=tmp;} }}classTest{staticvoidMain(){ Myclassm=newMyclass(); inta,b,c; a=30;b=20;c=10; m.Sort(a,b,c); Console.WriteLine("a={0},b={1},c={2}",a,b,c); Console.Read();}}【例3.7】

下面的程序演示了當方法Sort傳遞的是值參數(shù)時,對形參的修改不影響其實參。

a、b、c變量的值并沒有發(fā)生改變,因為它們都是按值傳給形參x、y、z的,形參x、y、z的變化并不影響外部a、b、c的值。但如果給方法傳遞的是一個引用對象時,它遵循的仍是值參數(shù)傳遞方式,形參另外分配一塊內(nèi)存,接受實參的引用值副本,同樣對引用值的修改不會影響外面的實參。但是,如果改變參數(shù)所引用的對象將會影響實參所引用的對象,事實上,它們是同一塊內(nèi)存區(qū)域。程序運行結果如下:usingSystem;classMyclass{publicvoidSortArray(int[]a){inti,j,pos,tmp;for(i=0;i<a.Length1;i++){for(pos=j=i;j<a.Length;j++)if(a[pos]>a[j])pos=j;if(pos!=i){tmp=a[i]; a[i]=a[pos]; a[pos]=tmp;}}}}classTest{staticvoidMain(){Myclassm=newMyclass(); int[]score={87,89,56,90,100,75,64,45,80,84};m.SortArray(score);for(inti=0;i<score.Length;i++){Console.Write("score[{0}]={1},",i,score[i]);if(i==4)Console.WriteLine(); } Console.Read();}}【例3.8】

下面程序演示的是當方法傳遞的是一個引用對象(如數(shù)組)時,對形參的修改會影響到實參。2.引用參數(shù)如果調(diào)用一個方法,期望能夠?qū)鬟f給它的實際變量進行操作,如下面例3.9中Sort方法對x、y、z的排序希望對調(diào)用這個方法的實際變量a、b、c產(chǎn)生作用,如前所見,用C#默認的按值傳遞是不可能實現(xiàn)的。所以C#用ref修飾符來解決此類問題,它告訴編譯器,實參與形參的傳遞方式是引用。引用與值參數(shù)不同,引用參數(shù)并不創(chuàng)建新的存儲單元,它與方法調(diào)用中的實在參數(shù)變量同處一個存儲單元。因此,在方法內(nèi)對形參的修改就是對外部實參變量的修改。程序運行結果如下:usingSystem;classMyclass{ publicvoidSort(refintx,refinty,refintz) { inttmp;//tmp是方法Sort的局部變量

//將x,y,z按從小到大排序

if(x>y){tmp=x;x=y;y=tmp;} if(x>z){tmp=x;x=z;z=tmp;} if(y>z){tmp=y;y=z;z=tmp;} }}

classTest{ staticvoidMain() { Myclassm=newMyclass(); inta,b,c; a=30;b=20;c=10; m.Sort(refa,refb,refc); Console.WriteLine("a={0},b={1},c={2}",a,b,c); Console.Read(); }}【例3.9】

將例3.7程序中Sort方法的值參數(shù)傳遞方式改成引用參數(shù)傳遞,這樣在方法Sort中對參數(shù)x、y、z按從小到大的排序影響了調(diào)用它的實參a、b、c。使用ref時請注意:(1)ref關鍵字僅對跟在它后面的參數(shù)有效,而不能應用于整個參數(shù)表。(2)在調(diào)用方法時,也用ref修飾實參變量,因為是引用參數(shù),所以要求實參與形參的數(shù)據(jù)類型必須完全匹配,而且實參必須是變量,不能是常量或表達式。(3)在方法外,ref參數(shù)必須在調(diào)用之前明確賦值,在方法內(nèi),ref參數(shù)被視為已賦過初始值。3.輸出參數(shù)在參數(shù)前加out修飾符的被稱為輸出參數(shù),它與ref參數(shù)相似,只有一點除外,就是它只能用于從方法中傳出值,而不能從方法調(diào)用處接受實參數(shù)據(jù)。在方法內(nèi)out參數(shù)被認為是未賦過值的,所以在方法結束之前應該對out參數(shù)賦值。程序運行結果如下:usingSystem;classMyclass{

publicvoidMaxMinArray(int[]a,outintmax,outintmin,outdoubleavg){ intsum; sum=max=min=a[0]; for(inti=1;i<a.Length;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; sum+=a[i]; } avg=sum/a.Length;}}classTest{staticvoidMain(){Myclassm=newMyclass(); int[]score={87,89,56,90,100,75,64,45,80,84}; intsmax,smin; doublesavg; m.MaxMinArray(score,outsmax,outsmin,outsavg); Console.Write("Max={0},Min={1},Avg={2}",smax,smin,savg); Console.Read(); }}【例3.10】

求一個數(shù)組中元素的最大值、最小值和平均值。ref和out參數(shù)的使用并不局限于值類型參數(shù),它們也可用于引用類型來傳遞對象。程序運行結果如下:usingSystem;classMyclass{

publicvoidSwap1(strings,stringt){ stringtmp; tmp=s; s=t; t=tmp;}

publicvoidSwap2(refstrings,refstringt){ stringtmp; tmp=s; s=t; t=tmp;}}classTest{staticvoidMain(){Myclassm=newMyclass();strings1="ABCDEFG",s2="134567";m.Swap1(s1,s2);Console.WriteLine("s1={0}",s1);//s1,s2的引用并沒有改變

Console.WriteLine("s2={0}",s2);m.Swap2(refs1,refs2); //s1,s2的引用互相交換了

Console.WriteLine("s1={0}",s1);Console.WriteLine("s2={0}",s2);Console.Read(); }}【例3.11】

下面程序定義了兩個方法,一個是Swap1,一個是Swap2,它們都有兩個引用對象作為參數(shù),但Swap2的參數(shù)加了ref修飾,調(diào)用這兩個方法產(chǎn)生的結果是不一樣的。4.參數(shù)數(shù)組一般而言,調(diào)用方法時其實參必須與該方法聲明的形參在類型和數(shù)量上相匹配,但有時候我們更希望靈活一些,C#提供了傳遞可變長度參數(shù)表的機制,使用params關鍵字來指定一個可變長的參數(shù)表。程序運行結果如下:usingSystem;classMyclass{

publicvoidMaxMin(outintmax,outintmin,paramsint[]a) { if(a.Length==0)//如果可變參數(shù)為零個,可以取一個約定值或產(chǎn)生異常

{ max=min=1; return; } max=min=a[0]; for(inti=1;i<a.Length;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; } }} classTest{

staticvoidMain()

{Myclassm=newMyclass(); int[]score={87,89,56,90,100,75,64,45,80,84}; intsmax,smin; m.MaxMin(outsmax,outsmin); //可變參數(shù)的個數(shù)可以是零個

Console.WriteLine("Max={0},Min={1}",smax,smin); m.MaxMin(outsmax,outsmin,45,76,89,90); //在4個數(shù)之間找最大、最小

Console.WriteLine("Max={0},Min={1}",smax,smin); m.MaxMin(outsmax,outsmin,score); //可變參數(shù)也可接受數(shù)組對象

Console.WriteLine("Max={0},Min={1}",smax,smin); Console.Read();}}【例3.12】

下面程序演示了Myclass類中的方法MaxMin有一個參數(shù)數(shù)組類型的參數(shù),在調(diào)用這個方法時具有靈活性。usingSystem;classMyclass{

publicvoidMaxMin(outintmax,outintmin,paramsint[]a){ if(a.Length==0)//如果可變參數(shù)為零個,可以取一個約定值或產(chǎn)生異常

{ max=min=1; return; } max=min=a[0]; for(inti=1;i<a.Length;i++) { if(a[i]>max)max=a[i]; if(a[i]<min)min=a[i]; }}}classTest{

staticvoidMain()

{Myclassm=newMyclass();int[]score={87,89,56,90,100,75,64,45,80,84};intsmax,smin;m.MaxMin(outsmax,outsmin);//可變參數(shù)的個數(shù)可以是零個Console.WriteLine("Max={0},Min={1}",smax,smin);m.MaxMin(outsmax,outsmin,45,76,89,90); //在4個數(shù)之間找最大、最小Console.WriteLine("Max={0},Min={1}",smax,smin);m.MaxMin(outsmax,outsmin,score);//可變參數(shù)也可接受數(shù)組對象Console.WriteLine("Max={0},Min={1}",smax,smin);Console.Read();}}【例3.12】

下面程序演示了Myclass類中的方法MaxMin有一個參數(shù)數(shù)組類型的參數(shù),在調(diào)用這個方法時具有靈活性。3.3.3靜態(tài)方法與實例方法方法聲明含有static關鍵字,稱為靜態(tài)方法只能對類的靜態(tài)成員操作,不可以訪問實例字段。cashRegister例子3.3.4方法的重載與覆蓋一個方法的名字和形式參數(shù)的個數(shù)、修飾符及類型共同構成了這個方法的簽名,同一個類中不能有相同簽名的方法。如果一個類中有兩個或兩個以上的方法同名,而它們的形參個數(shù)或形參類型有所不同是允許的,它們屬于不同的方法簽名。但是僅僅是返回類型不同的同名方法,編譯器是不能識別的?!纠?.14】

下面程序定義的Myclass類中含有4個名為max的方法,但它們或者參數(shù)個數(shù)不同,或者參數(shù)類型不同,在Main調(diào)用該方法時,編譯器會根據(jù)參數(shù)的個數(shù)和類型確定調(diào)用哪個max方法。usingSystem;classMyclass//該類中有max方法的4個不同版本

//它們或者參數(shù)類型不同,或者參數(shù)個數(shù)不同{publicintmax(intx,inty) { returnx>=y?x:y; } publicdoublemax(doublex,doubley) { returnx>=y?x:y; } publicintmax(intx,inty,intz) { returnmax(max(x,y),z); } publicdoublemax(doublex,doubley,doublez) { returnmax(max(x,y),z); }}

classTest{staticvoidMain()

{ Myclassm=newMyclass(); inta,b,c; doublee,f,g; a=10;b=20;c=30; e=1.5;f=3.5;g=5.5; //調(diào)用方法時,編譯器會根據(jù)實參的類型和個數(shù)調(diào)用不同的方法

Console.WriteLine("max({0},{1})={2}",a,b,m.max(a,b)); Console.WriteLine("max({0},{1},{2})={3}",a,b,c,m.max(a,b,c)); Console.WriteLine("max({0},{1})={2}",e,f,m.max(e,f)); Console.WriteLine("max({0},{1},{2})={3}",e,f,g,m.max(e,f,g)); Console.Read();}}程序運行結果如下:類Myclass中有4個同名的方法max,或參數(shù)個數(shù)不一樣,或參數(shù)類型不一樣。在調(diào)用max方法時,編譯器會根據(jù)調(diào)用時給出的實參個數(shù)及類型調(diào)用相應的方法,這就是編譯時實現(xiàn)的多態(tài)。在一個有繼承關系的類層次結構中,如果派生類與基類有相同名稱或簽名的成員,那么在派生類中就隱藏了基類成員,為了警示,編譯器會發(fā)出一個警告信息。如果派生類是有意隱藏基類成員,可在派生類成員聲明中加new修飾符,這樣可取消警告信息。usingSystem;classShape{protecteddoublewidth;protecteddoubleheight;publicShape(){width=height=0;}publicShape(doublex){width=height=x;}publicShape(doublew,doubleh){ width=w; height=h;}publicdoublearea(){ returnwidth*height; }}例3.15基類Shape派生類Triangle和Trapezianew修飾了area方法classTriangle:Shape //三角形{publicTriangle(doublex,doubley):base(x,y) { }newpublicdoublearea()//派生類方法與基類方法同名,編譯時會有警告信息

{ returnwidth*height/2; }}classTrapezia:Shape //梯形{doublewidth2;publicTrapezia(doublew1,doublew2,doubleh):base(w1,h){ width2=w2;}newpublicdoublearea()//加new隱藏基類的area方法

{ return(width+width2)*height/2;}}使用關鍵字new修飾方法,可以在一個繼承的結構中隱藏有相同簽名的方法?;悓ο驛被引用到派生類對象B時,它訪問的仍是基類的方法。更多的時候,我們期望根據(jù)當前所引用的對象來判斷調(diào)用哪一個方法,這個判斷過程是在運行時進行的。另一種更為靈活和有效的手段是,首先將基類的方法用關鍵字virtual修飾為虛方法,再由派生類用關鍵字override修飾與基類中虛方法有相同簽名的方法,表明是對基類的虛方法重載。后者的優(yōu)勢在于它可以在程序運行時再決定調(diào)用哪一個方法,這就是所謂的“運行時多態(tài)”,或者稱動態(tài)綁定?!纠?.16】

將例3.15改寫,Shape類中的方法area用virtual修飾,而派生類Triangle和Trapezia用關鍵字override修飾area方法,這樣就可以在程序運行時決定調(diào)用哪個類的area方法。usingSystem;classShape{protecteddoublewidth;protecteddoubleheight; publicShape(){ width=height=0;} publicShape(doublex){width=height=x;} publicShape(doublew,doubleh) { width=w; height=h; } publicvirtualdoublearea() //基類中用virtual修飾符聲明一個虛方法

{ returnwidth*height;}}classTriangle:Shape //三角形{ publicTriangle(doublex,doubley):base(x,y) { } publicoverridedoublearea() //派生類中用override修飾符覆蓋基類虛方法

{ returnwidth*height/2; }}classTrapezia:Shape //梯形{ doublewidth2; publicTrapezia(doublew1,doublew2,doubleh):base(w1,h) { width2=w2; } publicoverridedoublearea() //派生類中用override修飾符覆蓋基類虛方法

{ return(width+width2)*height/2; }}classTest{ staticvoidMain()

{ //此處代碼同上例,在此省略。

}}

由于area方法在基類被定義為虛方法又在派生類中被覆蓋,所以當基類的對象引用A被引用到派生類對象時,調(diào)用的就是派生類覆蓋的area方法。在類的層次結構中,只有使用override修飾符,派生類中的方法才可以覆蓋(重載)基類的虛方法,否則就是隱藏基類的方法有關虛方法的使用,請注意:(1)不能將虛方法聲明為靜態(tài)的,因為多態(tài)性是針對對象的,不是針對類的。(2)不能將虛方法聲明為私有的,因為私有方法不能被派生類覆蓋。(3)覆蓋方法必須與它相關的虛方法匹配,也就是說,它們的方法簽名(方法名稱、參數(shù)個數(shù)、參數(shù)類型)、返回類型及訪問屬性等都應該完全一致。(4)一個覆蓋方法覆蓋的必須是虛方法,3.4屬性為了實現(xiàn)良好的數(shù)據(jù)封裝和數(shù)據(jù)隱藏,類的字段成員的訪問屬性一般設置成private或protected,這樣在類的外部就不能直接讀/寫這些字段成員了,通常的辦法是提供public級的方法來訪問私有的或受保護的字段。但C#提供了屬性(property)這個更好的方法,把字段域和訪問它們的方法相結合。對類的用戶而言,屬性值的讀/寫與字段域語法相同;對編譯器來說,屬性值的讀/寫是通過類中封裝的特別方法get訪問器和set訪問器實現(xiàn)的。屬性的聲明方法如下:語法形式:[屬性集信息][屬性修飾符]類型成員名{

訪問器聲明}其中:屬性修飾符——與方法修飾符相同,包括new、static、virtual、abstract、override和4種訪問修飾符的合法組合,它們遵循相同的規(guī)則。類型——指定該聲明所引入的屬性的類型。成員名——指定該屬性的名稱。訪問器聲明——聲明屬性的訪問器,可以是一個get訪問器或一個set訪問器,或者兩個都有。語法形式:get //讀訪問器{ … //訪問器語句塊}set //寫訪問器{ … //訪問器語句塊}get訪問器的返回值類型與屬性的類型相同,所以在語句塊中的return語句必須有一個可隱式轉(zhuǎn)換為屬性類型的表達式。set訪問器沒有返回值,但它有一個隱式的值參數(shù),其名稱為value,它的類型與屬性的類型相同。同時包含get和set訪問器的屬性是讀/寫屬性,只包含get訪問器的屬性是只讀屬性,只包含set訪問器的屬性是只寫屬性。【例3.17】

對TextBox類的text、fontname、fontsize、multiline域提供屬性方式的讀/寫訪問。usingSystem;classTextBox{ privatestringtext; privatestringfontname; privateintfontsize; privateboolmultiline; publicTextBox() { text="text1"; fontname="宋體"; fontsize=12; multiline=false; } publicstringText {//Text屬性,可讀可寫

get { returntext; } set { text=value; } }publicstringFontName {//FontName屬性,只讀屬性

get { returnfontname; } } publicintFontSize {//FontSize屬性,可讀可寫

get { returnfontsize; } set { fontsize=value; } } publicboolMultiLine {//MultiLine屬性,只寫

set { multiline=value; } }}classTest{ staticvoidMain() { TextBoxText1=newTextBox(); //調(diào)用Text屬性的get訪問器

Console.WriteLine("Text1.Text={0}",Text1.Text); Text1.Text="這是文本框";//調(diào)用Text屬性的set訪問器

Console.WriteLine("Text1.Text={0}",Text1.Text); Console.WriteLine("Text1.Fontname={0}",Text1.FontName); Text1.FontSize=36; Text1.MultiLine=true; Console.WriteLine("Text1.FontSize={0}",Text1.FontSize); Console.Read(); }}程序運行結果如下:在這個示例中,類TextBox定義了4個屬性,在類體外使用這些屬性是用Text1.Text,Text1.FontName等形式,它們和字段域的訪問非常類似。編譯器根據(jù)它們出現(xiàn)的位置調(diào)用不同的訪問器,如果在表達式中引用該屬性則調(diào)用get訪問器,如果給屬性賦值則調(diào)用set訪問器,賦值號右邊的表達式值傳給value。屬性是字段的自然擴展,當然屬性也可作為特殊的方法使用,并不要求它和字段域一一對應,所以屬性還可以用于各種控制和計算。【例3.18】定義Label類,設置Width和Heigh屬性,分別存放兩點之間在水平坐標軸和垂直坐標軸上的投影長度。usingSystem;classPoint{ intx,y; publicintX { get { returnx; } } publicintY { get { returny; } } publicPoint() { x=y=0; } publicPoint(intx,inty) { this.x=x; this.y=y; }}classLabel{ Pointp1=newPoint(); Pointp2=newPoint(5,10); publicintWidth //計算兩點之間在水平坐標軸上的投影長度

{ get { returnp2.Xp1.X; } } publicintHeight //計算兩點之間在垂直坐標軸上的投影長度

{ get { returnp2.Yp1.Y; } }}classTest{ staticvoidMain() { LabelLabel1=newLabel(); Console.WriteLine("Label1.Width={0}",Label1.Width); //Label1.Width=5 Console.WriteLine("Label1.Height={0}",Label1.Height); //Label2.Height=10 Console.Read(); }}注意:盡管屬性與字段域有相同的使用語法,但它本身并不代表字段域。屬性不直接對應存儲位置,所以不能把它當變量使用,不能把屬性作為ref或者out參數(shù)傳遞。屬性和方法一樣,也有靜態(tài)修飾,在靜態(tài)屬性的訪問器中不能訪問靜態(tài)數(shù)據(jù)也不能引用this。3.5綜合應用實例【例3.19】

學生成績管理程序。根據(jù)學生選修的課程及課程學分和課程成績計算GPA,最后按GPA的值對學生進行排序?;舅悸罚罕境绦虻膶W生總?cè)藬?shù)、課程名、課程學分可以由控制臺輸入,為敘述簡單,假定每個學生所選修的課程相同。Course類定義了課程名、課程學分字段域,并使用屬性公開私有字段。另外,Course類還定義了Name屬性、構造函數(shù)。Course類代碼如下:classCourse{ stringcourseName; //課程名

intcourseMark; //課程學分

publicCourse() { } publicCourse(stringName,intMark) { courseName=Name; courseMark=Mark; } publicstringName //Name屬性,課程名可讀可寫

{ get { returncourseName; } set { courseName=value; } }publicintMark //Mark屬性,課程學分可讀可寫

{ get { returncourseMark; } set { courseMark=value; } }}Student類定義學生姓名、學號、選修課程數(shù)、Course類、成績及GPA等字段,并使屬性公開(public)。假定選修課程一樣,所以將課程數(shù)、Course類對象定義為static字段,不需要每個學生都有這份數(shù)據(jù)副本。Student類還定義了CourseNum靜態(tài)屬性、GPA屬性、Name屬性,SetCourse方法,用于設置課程名,因為不需要為每個學生設置,所以定義成靜態(tài)方法。AddData屬性給每個學生加入姓名、學號、成績。ComputeGPA方法計算學生成績的GPA。stuSwap方法對兩個Student對象內(nèi)容進行交換。Student類的代碼如下:classStudent{ stringstuName; //學生姓名

stringstuID; //學生學號

staticintnumberOfCourse; //加static修飾符表明這個域為所有學生類對象共享

staticCourse[]list; //Course類對象數(shù)組,用于設置每門課程名、課程學分

int[]stuScore; //每個學生對象要填寫的各課程成績

doublestuGPA; //GPA值

publicStudent() {//當?shù)谝淮蝿?chuàng)建Student對象時,創(chuàng)建list對象數(shù)組,并初始化

list=newCourse[numberOfCourse]; for(inti=0;i<numberOfCourse;i++) list[i]=newCourse(); stuScore=newint[numberOfCourse]; }//將CourseNum定義成靜態(tài)屬性是因為它只對靜態(tài)域進行操作

publicstaticintCourseNum{ get { returnnumberOfCourse; } set { numberOfCourse=value; } } publicdoubleGPA//GPA屬性是只讀屬性

{ get { returnstuGPA; } } publicstringName//Name屬性可讀可寫

{ get { returnstuName; } set { stuName=value; } }//將SetCourse設為靜態(tài)方法,是因為它僅訪問靜態(tài)數(shù)據(jù)域//不需要創(chuàng)建Student類對象就可直接用Student類名調(diào)用//它的形參是一個參數(shù)數(shù)組,這樣調(diào)用時就可根據(jù)實際選修的課程數(shù)來設置

publicstaticvoidSetCourse(paramsCourse[]topic) { for(inti=0;i<topic.Length;i++) { list[i].Name=topic[i].Name; list[i].Mark=topic[i].Mark; } }//AddData方法將一個學生的數(shù)據(jù)添加到學生類對象數(shù)組中

publicvoidAddData(stringname,stringId,int[]score) { stuName=name; stuID=Id; for(inti=0;i<score.Length;i++) stuScore[i]=score[i]; }publicvoidComputeGPA()//根據(jù)課程的學分以及學生成績計算GPA { inti; doublesMark,sumMark=0,sumGP=0; for(i=0;i<stuScore.Length;i++) { if(stuScore[i]>=95) sMark=4.5; elseif(stuScore[i]>=90) sMark=4; elseif(stuScore[i]>=85) sMark=3.5;elseif(stuScore[i]>=80) sMark=3; elseif(stuScore[i]>=75) sMark=2.5; elseif(stuScore[i]>=70) sMark=2; elseif(stuScore[i]>=65) sMark=1.5; elseif(stuScore[i]==60) sMark=1; else sMark=0; sumGP+=list[i].Mark*sMark; sumMark+=list[i].Mark; } stuGPA=sumGP/sumMark; }//stuSwap方法提供兩個Student類對象的交換操作,注意它們的形參被修飾為refpublicvoidstuSwap(refStudentstu1,refStudentstu2) {

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論