章面向?qū)ο缶幊踢M(jìn)階_第1頁
章面向?qū)ο缶幊踢M(jìn)階_第2頁
章面向?qū)ο缶幊踢M(jìn)階_第3頁
章面向?qū)ο缶幊踢M(jìn)階_第4頁
章面向?qū)ο缶幊踢M(jìn)階_第5頁
已閱讀5頁,還剩134頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第4章面向?qū)ο缶幊踢M(jìn)階4.1類的繼承與多態(tài)當(dāng)今的面向?qū)ο缶幊陶Z言都提供了繼承和多態(tài)的功能,C#作為一種面向?qū)ο蟮母呒壘幊陶Z言也具有這樣的特點。繼承是面向?qū)ο笳Z言的基本特征,是實現(xiàn)代碼復(fù)用的手段。繼承使得在原有的類基礎(chǔ)之上,對原有的程序進(jìn)行擴(kuò)展,從而提高程序開發(fā)的速度,實現(xiàn)代碼的復(fù)用。同一種方法作用于不同對象可以產(chǎn)生不同的結(jié)果,這就是多態(tài)性。它是在基類中使用虛方法,在其派生類中使用重載實現(xiàn)的。4.1.1繼承1.使用繼承繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進(jìn)行擴(kuò)展。使用繼承而產(chǎn)生的類被稱為派生類或者子類,而被繼承的類則稱為基類、超類或父類??陀^世界中的許多事物之間往往都是具有相同的特征,具有繼承的特點。圖4.1和圖4.2是兩個采用類的層次圖表示繼承的例子。1.使用繼承圖4.1類的層次結(jié)構(gòu)1圖4.2類的層次結(jié)構(gòu)24.1.1繼承【例4.1】類的繼承。usingSystem;//定義基類ShapepublicclassShape{ protectedstringColor; publicShape() {;} publicShape(stringColor) { this.Color=Color; } publicstringGetColor() { returnColor; }}【例4.1】//定義Circle類,從Shape類中派生publicclassCircle:Shape{ privatedoubleRadius; publicCircle(stringColor,doubleRadius) { this.Color=Color; this.Radius=Radius; } publicdoubleGetArea() { returnSystem.Math.PI*Radius*Radius; }}【例4.1】//派生類Rectangular,從Shape類中派生publicclassRectangular:Shape{ protecteddoubleLength,Width; publicRectangular() {Length=Width=0;} publicRectangular(stringColor,doubleLength,doubleWidth) { this.Color=Color; this.Length=Length; this.Width=Width; } publicdoubleAreaIs() { returnLength*Width; }【例4.1】publicdoublePerimeterIs()//周長

{ return(2*(Length+Width)); }}//派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide) { this.Color=Color; this.Length=this.Width=Side; }}publicclassTestInheritance{ publicstaticvoidMain()【例4.1】{ CircleCir=newCircle("orange",3.0); Console.WriteLine("Circlecoloris{0},Circleareais{1}",Cir.GetColor(),Cir.GetArea()); RectangularRect=newRectangular("red",13.0,2.0); Console.WriteLine("Rectangualrcoloris{0},Rectangualrareais{1},Rectangularperimeteris{2}",Rect.GetColor(),Rect.AreaIs(),Rect.PerimeterIs()); SquareSqu=newSquare("green",5.0); Console.WriteLine("Squarecoloris{0},SquareAreais{1},Squareperimeteris{2}",Squ.GetColor(),Squ.AreaIs(),Squ.PerimeterIs()); }}

運行結(jié)果如圖4.3所示。圖4.3運行結(jié)果4.1.1繼承2.base關(guān)鍵字例4.1程序中的square類也可以改寫為://派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide):base(Color,Side,Side) {;}}關(guān)鍵字base的作用是調(diào)用Rectangular類的構(gòu)造函數(shù)并將Square類的變量初始化。如果將Square類改寫成://派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide) {;}}2.base關(guān)鍵字實際上這種情況調(diào)用的是父類的無參構(gòu)造函數(shù),而不是有參構(gòu)造函數(shù),等同于://派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide):base() {;}}base關(guān)鍵字除了能調(diào)用基類對象構(gòu)造函數(shù),還可以調(diào)用基類的方法。在下例中,Employee類的GetInfoEmployee方法使用了base關(guān)鍵字調(diào)用基類Person的GetInfoPerson方法。2.base關(guān)鍵字【例4.2】base調(diào)用基類的方法。usingSystem;publicclassPerson{ protectedstringPhone=""; protectedstringName="李明"; publicvoidGetInfoPerson() { Console.WriteLine("Phone:{0}",Phone); Console.WriteLine("Name:{0}",Name); }}classEmployee:Person{ publicstringID="ABC567EFG"; publicvoidGetInfoEmployee()【例4.2】{ //調(diào)用基類Person的GetInfo方法

base.GetInfoPerson(); Console.WriteLine("EmployeeID:{0}",ID); }}classTestClass{ publicstaticvoidMain() { EmployeeEmployees=newEmployee(); Employees.GetInfoEmployee(); }}4.1.1繼承3.繼承中的構(gòu)造函數(shù)與析構(gòu)函數(shù)在前面討論過派生類的構(gòu)造函數(shù)會隱式調(diào)用父類的無參構(gòu)造函數(shù)。那么,如果父類也是派生于其它類,會是什么樣的情形呢?C#的執(zhí)行順序是這樣的:根據(jù)層次鏈找到最頂層的基類,先調(diào)用基類的構(gòu)造函數(shù),再依次調(diào)用各級派生類的構(gòu)造函數(shù)。而析構(gòu)函數(shù)的次序正好相反?!纠?.3】繼承中的構(gòu)造函數(shù)與析構(gòu)函數(shù)。usingSystem;publicclassBaseLifeSample{ publicstaticvoidMain() { Sons1=newSon(); }}publicclassGrandsire【例4.3】{ publicGrandsire() { Console.WriteLine("調(diào)用Grandsire的構(gòu)造函數(shù)"); } ~Grandsire() { Console.WriteLine("調(diào)用Grandsire的析構(gòu)函數(shù)"); }}publicclassFather:Grandsire{ publicFather() { Console.WriteLine("調(diào)用Father的構(gòu)造函數(shù)"); } ~Father() { Console.WriteLine("調(diào)用Father的析構(gòu)函數(shù)"); }}【例4.3】publicclassSon:Father{ publicSon() { Console.WriteLine("調(diào)用Son的構(gòu)造函數(shù)"); } ~Son() { Console.WriteLine("調(diào)用Son的析構(gòu)函數(shù)"); }}

運行結(jié)果如圖4.4所示。圖4.4運行結(jié)果4.1.1繼承4.System.Object類C#所有類都派生于System.Object類。在定義類時如果沒有指定派生于哪一個類,系統(tǒng)就默認(rèn)其派生于Object類。例4.2中Shape的定義就等同于:publicclassShape:System.Object{ …} System.Object類常見的公有方法是:

Equals:如果兩個對象具有相同值時,方法將返回true。

GetHashCode:方法返回對象的值的散列碼。

ToString:通過在派生類中重寫該方法,返回一個表示對象狀態(tài)的字符串。4.1.2多態(tài)多態(tài)也是面向?qū)ο笳Z言的基本特征之一,是指在程序執(zhí)行之前無法根據(jù)函數(shù)名和參數(shù)確定調(diào)用哪一個操作,而是程序執(zhí)行過程中,根據(jù)實際運行情況動態(tài)確定,從而帶來編程高度的靈活性。實現(xiàn)多態(tài)的方法是使用虛方法。1.虛方法的重載在類的方法前加上關(guān)鍵字virtual,則該方法被稱為虛方法。通過對虛方法的重載,實現(xiàn)在程序運行過程中確定調(diào)用的方法。需要注意的是這里所講的重載與第3章所講的通過參數(shù)類型與參數(shù)個數(shù)的不同實現(xiàn)的重載含義是不同的?!纠?.4】虛方法的重載。usingSystem;classA{ publicvoidF(){Console.WriteLine("A.F");} publicvirtualvoidG(){Console.WriteLine("A.G");}}【例4.4】classB:A{ newpublicvoidF(){Console.WriteLine("B.F");} publicoverridevoidG(){Console.WriteLine("B.G");}}classTest{ staticvoidMain() { Bb=newB(); Aa=b; a.F(); b.F(); a.G(); b.G(); }}【例4.4】

在A類定義了提供了非虛的F和虛方法G,派生類B則對方法F實現(xiàn)覆蓋,對虛方法G實現(xiàn)了虛方法的的重載。“Aa=b”實際上a仍舊是一個b對象。輸出結(jié)果為:

A.F B.FB.GB.G

在例4.1中計算圓形和矩形的面積分用了兩個不同的方法GetArea和AreaIs,也可以通過虛方法實現(xiàn)。將Shape中增加虛方法:

publicvirtualdoubleGetArea() { return0.0; }

在Circle類中對虛方法重載:

publicoverridedoubleGetArea() { returnSystem.Math.PI*radius*radius; }

在Rectangular類中對虛方法重載:

publicoverridedoubleGetArea() { returnLength*Width;}4.1.2多態(tài)【例4.5】用虛方法的實現(xiàn)重載usingSystem;//定義基類ShapepublicclassShape{ protectedstringColor; publicShape() {;} publicShape(stringColor) { this.Color=Color; } publicstringGetColor() { returnColor; } publicvirtualdoubleGetArea() { return0.0; }}【例4.5】//定義Circle類,從Shape類中派生publicclassCircle:Shape{ privatedoubleRadius; publicCircle(stringColor,doubleRadius) { this.Color=Color; this.Radius=Radius; } publicoverridedoubleGetArea() { returnSystem.Math.PI*Radius*Radius; }}//派生類Rectangular,從Shape類中派生publicclassRectangular:Shape【例4.5】{ protecteddoubleLength,Width; publicRectangular(stringColor,doubleLength,doubleWidth) { this.Color=Color; this.Length=Length; this.Width=Width; } publicoverridedoubleGetArea() { returnLength*Width; } publicdoublePerimeterIs() //周長

{ return(2*(Length+Width)); }}【例4.5】//派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide):base(Color,Side,Side) {;}}publicclassTestInheritance{ publicstaticvoidMain() { CircleCir=newCircle("orange",3.0); Console.WriteLine("Circlecoloris{0},Circleareais{1}",Cir.GetColor(),Cir.GetArea()); RectangularRect=newRectangular("red",13.0,2.0); Console.WriteLine("Rectangualrcoloris{0},Rectangualrareais{1},Rectangularperimeteris【例4.5】{2}",Rect.GetColor(),Rect.GetArea(),Rect.PerimeterIs()); SquareSqu=newSquare("green",5.0); Console.WriteLine("Squarecoloris{0},SquareAreais{1},Squareperimeteris{2}",Squ.GetColor(),Squ.GetArea(),Squ.PerimeterIs()); ShapeShp=Cir; Console.WriteLine("Circleareais{0}",Shp.GetArea()); Shp=Rect; Console.WriteLine("Rectangualrareais{0}",Shp.GetArea()); }}4.1.2多態(tài)2.抽象類與抽象方法抽象類是一種特殊的基類,并不與具體的事物聯(lián)系。抽象類的定義使用關(guān)鍵字abstract。在圖4.2類的層次結(jié)構(gòu)中,并沒有“圖形”這樣具體事物,所以可以將“圖形”定義為抽象類,派生了“圓形”和“四邊形”這樣一些可以產(chǎn)生具體實例化的普通類。需要注意的是,抽象類是不能被實例化,它只能作為其它類的基類。將Shape類定義為抽象類:publicabstractclassShape{…}在抽象類中也可以使用關(guān)鍵字abstract定義抽象方法,要求所有的派生非抽象類都要重載實現(xiàn)抽象方法。引入抽象方法的原因在于抽象類本身是一種抽象的概念,有的方法并不要具體的實現(xiàn),而是留下來讓派生類來重載實現(xiàn)。Shape類中GetArea方法本身沒什么具體的意義,而只有到了派生類Circle類和Rectangular才可以計算具體的面積。抽象方法寫法:publicabstractdoubleGetArea();則派生類重載實現(xiàn)為:publicoverridedoubleGetArea(){ …}4.1.2多態(tài)【例4.6】抽象類和抽象方法的實現(xiàn)usingSystem;//定義基類ShapepublicabstractclassShape{ protectedstringColor; publicShape() {;} publicShape(stringColor) { this.Color=Color; } publicstringGetColor() { returnColor; } publicabstractdoubleGetArea();}【例4.6】//定義Circle類,從Shape類中派生publicclassCircle:Shape{ privatedoubleRadius; publicCircle(stringColor,doubleRadius) { this.Color=Color; this.Radius=Radius; } publicoverridedoubleGetArea() { returnSystem.Math.PI*Radius*Radius; }}【例4.6】//派生類Rectangular,從Shape類中派生publicclassRectangular:Shape{ protecteddoubleLength,Width; publicRectangular(stringColor,doubleLength,doubleWidth) { this.Color=Color; this.Length=Length; this.Width=Width; } publicoverridedoubleGetArea() { returnLength*Width; } publicdoublePerimeterIs() //周長

{ return(2*(Length+Width)); }}【例4.6】//派生類Square,從Rectangular類中派生publicclassSquare:Rectangular{ publicSquare(stringColor,doubleSide):base(Color,Side,Side) {;}}publicclassTestInheritance{ publicstaticvoidMain() { CircleCir=newCircle("orange",3.0); Console.WriteLine("Circlecoloris{0},Circleareais{1}",Cir.GetColor(),Cir.GetArea()); RectangularRect=newRectangular("red",13.0,2.0); 【例4.6】Console.WriteLine("Rectangualrcoloris{0},Rectangualrareais{1},Rectangularperimeteris{2}",Rect.GetColor(),Rect.GetArea(),Rect.PerimeterIs()); SquareSqu=newSquare("green",5.0); Console.WriteLine("Squarecoloris{0},SquareAreais{1},Squareperimeteris{2}",Squ.GetColor(),Squ.GetArea(),Squ.PerimeterIs()); }}4.1.2多態(tài)3.密封類和密封方法抽象類是作為基類,不能被實例化,由其它類繼承。相對應(yīng)的還有一種不能被其它類繼承的類,叫密封類,使用sealed關(guān)鍵字定義。如果Rectangular類定義為密封類

:publicclassrectangular:Shape{…}這樣Rectangular類的派生類Square就不再保留,否則,就會出錯。如果類的方法聲明包含sealed修飾符,稱該方法為密封方法。類的實例方法聲明包含sealed修飾符,則必須同時使用override修飾符。使用密封方法可以防止派生類進(jìn)一步重寫該方法。如果將圓形Circle類的GetArea方法定義為密封類,必須先將Shape類GetArea方法定義為:publicvirtualdoubleGetArea(){ …}然后在Circle類中實現(xiàn)密封方法:publicsealedoverridedoubleGetArea(){ …}4.2操作符重載如果有一個復(fù)數(shù)Complex類對一元操作符“++”重載,可以寫成:

publicstaticComplexoperator++(Complexa) { … }對二元操作符“+”可以寫成:publicstaticComplexoperator+(Complexa,Complexb) { … }4.2操作符重載一元操作符有一個參數(shù),二元操作符有二個參數(shù)。重載操作符開始必須以publicstatic修飾??梢灾剌d的操作符包括:一元操作符:+-!~++--truefalse二元操作符:+-*/%&|^<<>>==!=><>=<=下面的操作符要求同時重載,不能重載中間的單獨一個:一元操作符:true和false二元操作符:==和!=,>和<,>=和<=操作符重載給類的一些操作帶來了方便。兩個復(fù)數(shù)的實部相加運算寫成:publicstaticdoubleAdd(complexa,complexb){ returna.r+b.r}這樣的寫法不夠簡潔,并且類的成員修飾符不為public時就不能這樣直接的操作。4.2操作符重載【例4.7】操作符重載的實現(xiàn)usingSystem;classComplex{ doubler,v; //r+vi publicComplex(doubler,doublev) { this.r=r; this.v=v; } //二元操作符”+”重載

publicstaticComplexoperator+(Complexa,Complexb) { returnnewComplex(a.r+b.r,a.v+b.v); }【例4.7】//一元操作符”-”重載

publicstaticComplexoperator-(Complexa) { returnnewComplex(-a.r,-a.v); } //一元操作符”++”重載

publicstaticComplexoperator++(Complexa) { doubler=a.r+1; doublev=a.v+1; returnnewComplex(r,v); }publicvoidPrint() { Console.Write(r+"+"+v+"i\n"); }}【例4.7】classTest{ publicstaticvoidMain() { Complexa=newComplex(3,4); Complexb=newComplex(5,6); Complexc=-a; c.Print(); Complexd=a+b; d.Print(); a.Print(); Complexe=a++; //先賦值后++ a.Print(); e.Print(); Complexf=++a; //先++后賦值

a.Print(); f.Print(); }}4.2操作符重載在操作符重載中,返回值往往需要“new”一個新的complex對象。另外一種的操作符重載類型是用戶定義的數(shù)據(jù)類型轉(zhuǎn)換,它實現(xiàn)不同類型之間的轉(zhuǎn)換,包括顯式轉(zhuǎn)換和隱式轉(zhuǎn)換兩種方式。編程中往往需要將一個類型轉(zhuǎn)換成另外一個類型。例如將int轉(zhuǎn)換成double,它們是系統(tǒng)已經(jīng)預(yù)定義的類型,編譯器知道如何來執(zhí)行它們的轉(zhuǎn)換,具體內(nèi)容在下一節(jié)“類型轉(zhuǎn)換”中討論。如果它們中間有的類型不是編譯器預(yù)定義的類型,編譯器將不知道執(zhí)行轉(zhuǎn)換,解決的方法是使用用戶定義的數(shù)據(jù)類型轉(zhuǎn)換。轉(zhuǎn)換過程不會丟失數(shù)據(jù)而出現(xiàn)異常,就采用隱式轉(zhuǎn)換。如果轉(zhuǎn)換過程有可能丟失數(shù)據(jù),就要采用顯式轉(zhuǎn)換。隱式類型轉(zhuǎn)換的寫法如:

publicstaticimplicitoperatorSquare(doubles) { … }實現(xiàn)double向Square轉(zhuǎn)換功能。關(guān)鍵字explicit實現(xiàn)顯式類型轉(zhuǎn)換:

publicstaticexplicitoperatordouble(Squares) { … }【例4.8】用戶定義的數(shù)據(jù)類型轉(zhuǎn)換。usingSystem;classSquare{ privatedoubleSide; publicSquare(ints) { Console.WriteLine("int類型參數(shù)構(gòu)造函數(shù)"); Side=(double)s; } publicSquare(doubles) { Console.WriteLine("double類型參數(shù)構(gòu)造函數(shù)"); Side=s; } //重寫object類的ToString()方法. publicoverridestringToString() { Console.WriteLine("重寫object類的ToString()方法"); returnthis.Side.ToString(); }【例4.8】 //重載”+”操作符,參數(shù)為兩個square類

publicstaticSquareoperator+(Squarex,Squarey) { Console.WriteLine("重載+操作符,參數(shù)為兩個square類"); returnnewSquare(x.Side+y.Side); } //重載”+”操作符,參數(shù)一個為square類,一個為double類型

publicstaticSquareoperator+(Squarex,doubley) { Console.WriteLine("重載+操作符,參數(shù)一個為square類,一個為double類型"); returnnewSquare(x.Side+y); } //重載”+”操作符,參數(shù)一個為square類,一個為int類型

publicstaticSquareoperator+(Squarex,inty) { Console.WriteLine("重載+操作符,參數(shù)一個為square類,一個為int類型"); returnx+(double)y; //調(diào)用上面的重載”+”操作符

}【例4.8】//隱式類型轉(zhuǎn)換,實現(xiàn)double類型轉(zhuǎn)換為Square publicstaticimplicitoperatorSquare(doubles) { Console.WriteLine("隱式類型轉(zhuǎn)換,實現(xiàn)double類型轉(zhuǎn)換為Square"); returnnewSquare(s); }//隱式類型轉(zhuǎn)換,實現(xiàn)int類型轉(zhuǎn)換為Square publicstaticimplicitoperatorSquare(ints) { Console.WriteLine("隱式類型轉(zhuǎn)換,實現(xiàn)int類型轉(zhuǎn)換為Square"); returnnewSquare((double)s); } //重載”==”操作符

publicstaticbooloperator==(Squarex,Squarey) { Console.WriteLine("重載==操作符,兩個參數(shù)都為square類"); returnx.Side==y.Side; }【例4.8】//重載”!=”操作符

publicstaticbooloperator!=(Squarex,Squarey) { Console.WriteLine("重載!=操作符,兩個參數(shù)都為square類"); return!(x==y); //調(diào)用前面的重載”==”操作符

} //當(dāng)重載”==”和”!=”的同時還要重載object類的GetHashCode()和Equals(),否則編譯器會出現(xiàn)警告

//也可以不重寫這兩個方法,對運行結(jié)果沒有影響

publicoverrideboolEquals(objecto) { returnthis==(Square)o; } publicoverrideintGetHashCode() { return(int)Side; }【例4.8】//重載”>”操作符

publicstaticbooloperator>(Squarex,Squarey) { Console.WriteLine("重載>操作符,兩個參數(shù)都為square類"); returnx.Side>y.Side; } //重載”<”操作符

publicstaticbooloperator<(Squarex,Squarey) { Console.WriteLine("重載<操作符,兩個參數(shù)都為square類"); returnx.Side<y.Side; } //重載”<=”操作符

publicstaticbooloperator<=(Squarex,Squarey) { Console.WriteLine("重載<=操作符,兩個參數(shù)都為square類"); return(x<y)||(x==y); //調(diào)用重載的操作符”==”和”<” }【例4.8】//重載”>=”操作符

publicstaticbooloperator>=(Squarex,Squarey) { Console.WriteLine("重載>=操作符,兩個參數(shù)都為square類"); return(x>y)||(x==y); //調(diào)用重載的操作符”==”和”>” } publicstaticvoidMain() { Squares1=newSquare(10); Squares2=newSquare(20); Squares3=s1+s2; //調(diào)用operator+(Square,Square) Console.WriteLine(s3); //調(diào)用重寫object類的ToString()方法

Console.WriteLine(s3+15); //調(diào)用重寫的operator+(Square,int)以及ToString() Console.WriteLine(s3+1.5); //調(diào)用重寫的operator+(Square,double)和ToString() s3=10; //調(diào)用隱式轉(zhuǎn)換publicstaticimplicitoperatorSquare(int) Console.WriteLine(s3); Squares4=10;【例4.8】Console.WriteLine(s1==s4); //調(diào)用==操作符

Console.WriteLine(s1!=s4); //調(diào)用!=操作符

Console.WriteLine(s1>s2); //調(diào)用>操作符

Console.WriteLine(s1<=s4); //調(diào)用<=操作符

}}運行結(jié)果如圖4.6所示。

Square與double和int之間有兩個隱式轉(zhuǎn)換。從double以及int隱式轉(zhuǎn)換過程不會丟失數(shù)據(jù)而出現(xiàn)異常,所以采用了隱式轉(zhuǎn)換。

Square類默認(rèn)的基類是Object類,所以可以重載Object類的方法ToString。WriteLine方法默認(rèn)的參數(shù)為String對象,所以在輸出之前都要調(diào)用ToString方法,將參數(shù)轉(zhuǎn)換為String類型?!纠?.8】圖4.6運行結(jié)果4.3類型轉(zhuǎn)換實際編程中,經(jīng)常用到類型的相互之間轉(zhuǎn)換問題,如一個int類型要轉(zhuǎn)換成一個long類型。類型轉(zhuǎn)換方法分為隱式類型轉(zhuǎn)換和顯式類型轉(zhuǎn)換,也可以采用Convert提供的方法實現(xiàn)類型轉(zhuǎn)換。4.3.1隱式類型轉(zhuǎn)換隱式類型轉(zhuǎn)換不需要加任何聲明就可以實現(xiàn)的類型轉(zhuǎn)換,規(guī)則簡單。1.隱式數(shù)值轉(zhuǎn)換數(shù)值轉(zhuǎn)換是指在整數(shù)類型、實數(shù)類型和字符類型之間的轉(zhuǎn)換。sbyte類型向int類型轉(zhuǎn)換是一種隱式數(shù)值類型轉(zhuǎn)換,轉(zhuǎn)換一般不會失敗,也不會丟失數(shù)據(jù)。如:sbytea=100;intb=a;隱式數(shù)值類型轉(zhuǎn)換如表4.1所示。從int到long,long到float、double等幾種類型轉(zhuǎn)換時可能導(dǎo)致精度的下降,但不導(dǎo)致數(shù)量上的丟失。從表4.1中可以看出,任何的原始類型,如果值的范圍完全包含在其它類型的值范圍內(nèi),那么就能進(jìn)行隱式轉(zhuǎn)換。需要注意的是char類型可以轉(zhuǎn)換為其它的整數(shù)或?qū)崝?shù)類型,但不存在其它類型轉(zhuǎn)換為char類型。1.隱式數(shù)值轉(zhuǎn)換表4.1隱式數(shù)值類型轉(zhuǎn)換1.隱式數(shù)值轉(zhuǎn)換【例4.9】隱式數(shù)值轉(zhuǎn)換。usingSystem;classReferenceConversion{ staticvoidMain() { chara='m'; intb=a; Console.WriteLine("aequals:{0}",a); Console.WriteLine("bequals:{0}",b); }}運行結(jié)果如下:aequals:m bequals:107如果這樣寫: intb=7; chara=b; Console.WriteLine("aequals:{0}",a); Console.WriteLine("bequals:{0}",b);編譯器將報錯:無法將類型“int”隱式轉(zhuǎn)換為“char”。4.3.1隱式類型轉(zhuǎn)換2.隱式枚舉轉(zhuǎn)換隱式枚舉轉(zhuǎn)換只允許將十進(jìn)制0轉(zhuǎn)換為枚舉類型的變量?!纠?.10】隱式枚舉轉(zhuǎn)換。usingSystem;enumColor{ Red,Green,Blue}classEnumConversion{ staticvoidMain() { Colora=Color.Red;Console.WriteLine("aequals:{0}",a); a=0; Console.WriteLine("aequals:{0}",a); }}

如果寫a=1或其它數(shù)值,編譯器提示:無法將類型“int”隱式轉(zhuǎn)換為“Color”。4.3.1隱式類型轉(zhuǎn)換3.隱式引用轉(zhuǎn)換類型s向類型t隱式引用轉(zhuǎn)換的條件是:s是從t派生來的,且s和t可以是接口或類。兩個數(shù)組的之間的隱式轉(zhuǎn)換的條件是:兩個數(shù)組的維數(shù)相同,元素都是引用類型,且存在數(shù)組元素的隱式引用轉(zhuǎn)換。例如:classEmployee //隱含繼承自System.Object{…}classApp{Employeee;objecto=e;…}使用隱式轉(zhuǎn)換的因為在于Employee是派生自O(shè)bject類。4.3.2顯式類型轉(zhuǎn)換顯式類型轉(zhuǎn)換只有在某些情況下實現(xiàn)轉(zhuǎn)換,規(guī)則復(fù)雜,需要用戶正確指定要轉(zhuǎn)換的類型。又稱強(qiáng)制類型轉(zhuǎn)換。1.顯式數(shù)值轉(zhuǎn)換int類型向byte類型轉(zhuǎn)換就是一種顯式數(shù)值類型轉(zhuǎn)換。例如:intb=100;sbytea=(byte)b;sbyte取值范圍是0~255,當(dāng)intb顯式轉(zhuǎn)換為sbyte時不會丟失信息。intb=1000;sbytea=(byte)b;則會出信息丟失,這是顯式數(shù)值轉(zhuǎn)換過程要注意的。顯式數(shù)值類型可轉(zhuǎn)換的類型如表4.2。1.顯式數(shù)值轉(zhuǎn)換表4.2顯式數(shù)值類型轉(zhuǎn)換4.3.2顯式類型轉(zhuǎn)換2.顯式枚舉轉(zhuǎn)換顯式枚舉轉(zhuǎn)換包括幾種情況:從sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal類型到任何枚舉類型;從任何枚舉類型到sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal;從任何枚舉類型到任何其它枚舉類型。顯式枚舉轉(zhuǎn)換其本質(zhì)是枚舉類型的元素類型與要轉(zhuǎn)換的類型之間的顯式轉(zhuǎn)換?!纠?.11】顯式枚舉轉(zhuǎn)換。usingSystem;enumColor{ Red, Green=10, Blue}classTest{ staticvoidMain()【例4.11】{ Console.WriteLine(StringFromColor(Color.Red)); Console.WriteLine(StringFromColor(Color.Green)); Console.WriteLine(StringFromColor(Color.Blue)); }//枚舉類型向整數(shù)類型顯式轉(zhuǎn)換

staticstringStringFromColor(Colorc) { switch(c) { caseColor.Red: //將指定的String中的每個格式項替換為相應(yīng)對象的值的文本等效項。

returnString.Format("Red={0}",(int)c); caseColor.Green: returnString.Format("Green={0}",(int)c); caseColor.Blue: returnString.Format("Blue={0}",(int)c);

【例4.11】default: return"Invalidcolor"; } }}運行結(jié)果如下:

Red=0Green=10Blue=114.3.2顯式類型轉(zhuǎn)換3.顯式引用轉(zhuǎn)換類型s向類型t顯式引用轉(zhuǎn)換的條件是t是從s派生來的,且s和t可以是接口或類。兩個數(shù)組的之間的隱式轉(zhuǎn)換的條件是兩個數(shù)組的維數(shù)相同,元素都是引用類型,且存在數(shù)組元素的顯式引用轉(zhuǎn)換。例如:classEmployee //隱含繼承自System.Object{ …}classApp{objectoEmployeee=(Employee)o;…}4.3.3使用Convert轉(zhuǎn)換System.Convert類中有一套的靜態(tài)方法實現(xiàn)類型的轉(zhuǎn)換,即使要轉(zhuǎn)換的類型之間沒有什么聯(lián)系也可以很方便的實現(xiàn)類型的轉(zhuǎn)換。包括的方法列在表4.3中。表4.3Convert類轉(zhuǎn)換的方法4.3.3使用Convert轉(zhuǎn)換【例4.12】System.Convert實現(xiàn)類型轉(zhuǎn)換。usingSystem; classTestConvert{ staticvoidMain() { shorta; try { Console.WriteLine("Enterastring:"); a=Convert.ToInt16(Console.ReadLine()); Console.WriteLine("aequals:{0}",a); } catch(FormatException) { Console.WriteLine("字符串不是由數(shù)字組成或為空."); } catch(OverflowException) { Console.WriteLine("字符串的數(shù)值超出了short范圍."); } }}4.4結(jié)構(gòu)與接口4.4.1結(jié)構(gòu)

C#中的結(jié)構(gòu)除了包含有數(shù)據(jù)成員,還有構(gòu)造函數(shù)、方法、屬性、事件、索引等成員,結(jié)構(gòu)也可以實現(xiàn)多個接口。結(jié)構(gòu)與類很類似,但也有很多的區(qū)別。首先結(jié)構(gòu)是值類型,而類是引用類型?!纠?.13】值類型的結(jié)構(gòu)。usingSystem;//定義結(jié)構(gòu) MyStructstructMyStruct{ //定義字段x,y publicintx; publicinty; //定義構(gòu)造函數(shù)

publicMyStruct(inti,intj) { x=i; y=j; }【例4.13】//定義方法

publicvoidSum() { intsum=x+y; Console.WriteLine("Thesumis{0}",sum); }}classClass1{ staticvoidMain() { MyStructs1=newMyStruct(1,2); MyStructs2=s1; s1.x=2; s1.Sum(); s2.Sum(); }}【例4.13】運行結(jié)果如下:

Thesumis4Thesumis3程序中的s2獲得了s1的數(shù)據(jù)一份拷貝,雖然s1.x值改變了,但并沒有影響到s2。當(dāng)兩個類的實例相等時,則表示它們指向同一段的內(nèi)存地址,試圖改變一個必然要影響到另一個。結(jié)構(gòu)實例化時也可以不用new。例如,Class1也可以寫成:classClass1{ staticvoidMain() { MyStructs1; s1.x=1; s1.y=2; MyStructs2=s1; s1.x=2; s1.Sum(); s2.Sum(); }}【例4.13】如果在Main()加入:

MyStructs3=newMyStruct();s3.Sum();

雖然結(jié)構(gòu)中沒有默認(rèn)的無參構(gòu)造函數(shù),但是卻可以調(diào)用無參構(gòu)造函數(shù),并且將值類型的數(shù)據(jù)成員設(shè)置為對應(yīng)值類型的缺省值,將其引用類型的數(shù)據(jù)成員設(shè)置為null。所以s3.x和s3.y都為0,s3最終的和也為0。結(jié)構(gòu)與類的區(qū)別如表4.4所示。表4.4結(jié)構(gòu)與類的區(qū)別4.4.2接口1.接口介紹接口是用來定義一種程序的協(xié)定。接口好比一種模版,這種模版定義了實現(xiàn)接口的對象必須實現(xiàn)的方法,其目的就是讓這些方法可以作為接口實例被引用。接口的定義如:

publicinterfaceIPartA { voidSetDataA(stringdataA); }接口使用關(guān)鍵字interface定義,接口可以使用的修飾符包括new,public,protected,internal,private等。接口的命名通常是以I開頭,如IPartA,IPartB。接口的成員可以是方法、屬性、索引器和事件,但不可以有任何的成員變量,也不能在接口中實現(xiàn)接口成員。接口不能被實例化。接口的成員默認(rèn)是公共的,因此不允許成員加上修飾符。4.4.2接口【例4.14】接口演示。usingSystem;//定義接口IPartApublicinterfaceIPartA{ voidSetDataA(stringdataA);}//定義接口IPartB,繼承IPartApublicinterfaceIPartB:IPartA{ voidSetDataB(stringdataB);}//定義類SharedClass,繼承接口IPartBpublicclassSharedClass:IPartB{ privatestringDataA; privatestringDataB;【例4.14】//實現(xiàn)接口IPartA的方法SetDataA publicvoidSetDataA(stringdataA) { DataA=dataA; Console.WriteLine("{0}",DataA); }//實現(xiàn)接口IPartB的方法SetDataB publicvoidSetDataB(stringdataB) { DataB=dataB; Console.WriteLine("{0}",DataB); }}【例4.14】classtest{ staticvoidMain() { SharedClassa=newSharedClass(); a.SetDataA("interfaceIPartA"); a.SetDataB("interfaceIPartB"); }}運行結(jié)果如下:interfaceIPartAinterfaceIPartB 程序中一共定義兩個接口和一個類。接口IPartA定義方法SetDataA,接口IPartB定義方法SetDataB。接口之間也有繼承關(guān)系,接口IPartB繼承接口IPartA,也就繼承了接口IPartA的SetDataA方法。接口只能定義方法,實現(xiàn)要由類或者結(jié)構(gòu)來完成。SharedClass類派生于接口IPartB,因此要實現(xiàn)IPartB的SetDataB方法,也要實現(xiàn)IPartA的SetDataA方法。4.4.2接口接口允許多重繼承:interfaceID:IA,IB,IC{ …}類可以同時有一個基類和零個以上的接口,并要將基類寫在前面:classClassB:ClassA,IA,IB{ …}4.4.2接口2.接口的實現(xiàn)指出接口成員所在的接口,則稱為顯式接口成員。上面程序接口實現(xiàn)可改寫成://沒有定義為publicvoidIPartA.SetDataA(stringdataA) { DataA=dataA; Console.WriteLine("{0}",DataA); }//沒有定義為publicvoidIPartB.SetDataB(stringdataB) { DataB=dataB; Console.WriteLine("{0}",DataB); }2.接口的實現(xiàn)顯式接口成員只能通過接口來調(diào)用。

classtest{ staticvoidMain() { SharedClassa=newSharedClass(); IPartBpartb=a; partb.SetDataA("interfaceIPartA"); partb.SetDataB("interfaceIPartB"); }}方法本身并不是類SharedClass提供,a.SetDataA("interfaceIPartA")或a.SetDataB("interfaceIPartB")調(diào)用都是錯誤的。顯式接口成員沒被聲明為public,這是因為這些方法都有著雙重的身份。當(dāng)在一個類中使用顯式接口成員時,該方法被認(rèn)為是私有方法,因此不能用類的實例調(diào)用它。但是,當(dāng)將類的引用轉(zhuǎn)型為接口引用時,接口中定義的方法就可以被調(diào)用,這時它又成為了一個公有方法。2.接口的實現(xiàn)【例4.15】顯式接口調(diào)用usingSystem;publicinterfaceIWindow{ ObjectGetMenu();}publicinterfaceIRestaurant{ ObjectGetMenu();}//該類型繼承自system.Object,并實現(xiàn)了IWindow和IRestaurant接口publicclassGiuseppePizzaria:IWindow,IRestaurant{ //該方法包括了IWindow接口的GetMenu方法實現(xiàn)

ObjectIWindow.GetMenu() { return"IWindow.GetMenu"; }【例4.15】//該方法包括了IRestaurant接口的GetMenu方法實現(xiàn)

ObjectIRestaurant.GetMenu() { return"IRestaurant.GetMenu"; } //這個GetMenu方法與接口沒有任何關(guān)系

publicObjectGetMenu() { return"GiuseppePizzaria.GetMenu"; } staticvoidMain() { //構(gòu)造一個類實例

GiuseppePizzariagp=newGiuseppePizzaria(); Objectmenu; //調(diào)用公有的GetMenu方法。使用GiuseppePizzaria引用, //顯式接口成員將為私有方法,因此不可能被調(diào)用。【例4.15】menu=gp.GetMenu(); Console.WriteLine(menu);//調(diào)用IWindow的GetMenu方法。使用IWindow引用, //因此只有IWindow.GetMenu方法被調(diào)用。

menu=((IWindow)gp).GetMenu(); Console.WriteLine(menu); //調(diào)用IRestaurant的GetMenu方法。使用IRestaurant引用, //因此只有IRestaurant.GetMenu方法被調(diào)用。

menu=((IRestaurant)gp).GetMenu(); Console.WriteLine(menu); }}

運行結(jié)果如下:GiuseppePizzaria.GetMenuIWindow.GetMenuIrestaurantGetMenu4.5集合與索引器4.5.1集合C#為用戶提供一種稱為集合的新類型。集合類似于數(shù)組,是一組組合在一起的類型化的對象,可以通過遍歷來訪問數(shù)組中的每個元素。.NET提供實現(xiàn)集合的接口,包括IEnumerable,ICollection,Ilist等,只需繼承實現(xiàn)集合接口。另外也可以直接使用.NET已經(jīng)定義的一些集合類,包括Array,ArrayList,Queue,Stack,BitArrary,Hashtable等。集合類是由命名空間System.Collections提供。1.自定義集合自定義集合是指實現(xiàn)System.Collections提供的集合接口的集合。下面以IEnumerable集合接口為例自定義集合。1.自定義集合IEnumerable接口定義:publicinterfaceIEnumerable{IenumeratorGetEnumerator();}實現(xiàn)IEnumerable的同時也要實現(xiàn)IEnumerator接口。IEnumerator接口為:publicinterfaceIEnumerator{ objectCurrent{get();} boolMoveNext(); voidReset();}1.自定義集合【例4.16】IEnumerable自定義集合。usingSystem;usingSystem.Collections;//集合類的命名空間namespaceCustomCollection{ //定義集合中的元素MyClass類

classMyClass { publicstringName; publicintAge; //帶參構(gòu)造器

publicMyClass(stringname,intage) { this.Name=name; this.Age=age; } }【例4.16】//實現(xiàn)接口Ienumerator和IEnumerable類Iterator publicclassIterator:IEnumerator,IEnumerable { //初如化MyClass類型的集合

privateMyClass[]ClassArray; intCnt; publicIterator() { //使用帶參構(gòu)造器賦值

ClassArray=newMyClass[4]; ClassArray[0]=newMyClass("Kith",23); ClassArray[1]=newMyClass("Smith",30); ClassArray[2]=newMyClass("Geo",19); ClassArray[3]=newMyClass("Greg",14); Cnt=-1; } 【例4.16】//實現(xiàn)IEnumerator的Reset()方法

publicvoidReset() { //指向第一個元素之前,Cnt為-1,遍歷是從0開始

Cnt=-1; } //實現(xiàn)IEnumerator的MoveNext()方法

publicboolMoveNext() { return(++Cnt<ClassArray.Length); } //實現(xiàn)IEnumerator的Current屬性

publicobjectCurrent { get { returnClassArray[Cnt]; } }【例4.16】//實現(xiàn)IEnumerable的GetEnumerator()方法

publicIEnumeratorGetEnumerator() { return(IEnumerator)this; } staticvoidMain() { IteratorIt=newIterator(); //像數(shù)組一樣遍歷集合

foreach(MyClassMYinIt) { Console.WriteLine("Name:"+MY.Name.ToString()); Console.WriteLine("Age:"+MY.Age.ToString()); } } }}【例4.16】運行結(jié)果如圖4.7所示。圖4.7運行結(jié)果4.5.1集合2.使用集合類 另一種使用集合的方法是使用系統(tǒng)已經(jīng)定義的集合類。下面就以Stack類為例介紹。Stack類表示對象的后進(jìn)先出集合。Stack類常用的方法有:Clear:從Stack中移除所有對象Pop:移除并返回位于Stack頂部的對象Push:將對象插入Stack的頂部Peek:返回位于Stack頂部的對象但不將其移除Stack類將它的對象存儲在數(shù)組。只要數(shù)組足夠大到可以存儲新的對象,調(diào)用Push方法就是非常有效的。但是如果內(nèi)部數(shù)組必須調(diào)整大小,就必須分配新數(shù)組并把現(xiàn)有的對象復(fù)制到新數(shù)組中。為了避免這一昂貴的操作成本,可以采用預(yù)選分配一個大的內(nèi)部數(shù)組,或定義滿足執(zhí)行需要的合適的增長系數(shù)。2.使用集合類【例4.17】Stack類的用法示例。usingSystem;usingSystem.Collections;classStackExample{ stati

溫馨提示

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

評論

0/150

提交評論