本文實例講述了C#中多態現象和多態的實現方法。分享給大家供大家參考。具體分析如下:
面向對象的特征封裝、繼承和多態。Polymorphism(多態性)來源于希臘單詞,指“多種形態”。多態性的一個重要特征是方法的調用是在運行時確定而不是編譯時。在.NET中用于實現多態性的關鍵詞有virtual、override、abstract、interface。
一、virtual實現多態
shape類是通用的基類,draw是一個虛方法,每個派生類都可以有自己的override版本,在運行時可以用shape類的變量動態的調用draw方法。
public class Shape{ public virtual void Draw() { Console.WriteLine("base class drawing"); }}public class Rectangle :Shape{ public override void Draw() { Console.WriteLine("Drawing a Rectangle"); }}public class Square :Rectangle{ public override void Draw() { Console.WriteLine("Drawing a Square"); base.Draw(); }}class Program{ static void Main(string[]args) { System.Collections.Generic.List<Shape> shapes =new List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(Shape s in shapes) { s.Draw(); } Console.ReadKey(); /*運行結果 Drawing a Rectangle Drawing a Square Drawing a Rectangle */ }}
方法、屬性、事件、索引器都可以被virtual修飾,但是字段不可以。派生類必須用override表示類成員參與虛調用。假如把Square中的draw方法替換為用new 修飾,則表示draw方法不參與虛調用,而且是一個新的方法,只是名字和基類方法重名。
public new void Draw(){ Console.WriteLine("Drawing a Square"); base.Draw();}
這個方法在Main方法中的foreach中將不會被調用,它不是虛方法了。用new修飾符后的程序運行結果,
/*運行結果Drawing a RectangleDrawing a Rectangle*/
假如說虛方法在rectangle擴展后,不希望square擴展了,可以在方法前加上sealed修飾符,
如下
public class Rectangle :Shape{ public sealed override voidDraw() { Console.WriteLine("Drawing a Rectangle"); }}
當派生類重寫某個虛擬成員時,即使該派生類的實例被當作基類的實例訪問或者把派生類實例賦給父類變量進行訪問,但是還是會調用派生類重寫后的成員,可以把代碼改為如下形式,
static void Main(string[] args){ System.Collections.Generic.List<Shape>shapes =new List<Shape>(); shapes.Add((Shape)new Rectangle()); shapes.Add((Shape)new Square()); foreach(Shape s inshapes) { s.Draw(); } Console.ReadKey(); /*運行結果 Drawing a Rectangle Drawing a Square Drawing a Rectangle */}
二、abstract實現多態
被abstract修飾的方法,默認是虛擬的,但是不能出現virtual關鍵詞修飾。被abstract修飾的類可以有已實現的成員,可以有自己的字段,可以有非abstract 修飾的方法,但是不能實例化因為抽象的東西是沒有實例對應的。比如,有人讓我們畫個圖形(抽象)是畫不出來的,但是讓畫個矩形(具體)是可以畫出來的。下面是用abstract實現的多態版本,
public abstract classShape{ public abstract void Draw();}public class Rectangle :Shape{ public override void Draw() { Console.WriteLine("Drawing a Rectangle"); }}public class Square :Rectangle{ public override void Draw() { Console.WriteLine("Drawing a Square"); base.Draw(); }}class Program{ static void Main(string[]args) { System.Collections.Generic.List<Shape>shapes =new List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(Shape s in shapes) { s.Draw(); } Console.ReadKey(); }}
被abstract修飾的方法,在派生類中同樣用override關鍵詞進行擴展。同樣可以用關鍵詞sealed阻止派生類進行擴展。
interface實現多態
接口可由方法、屬性、事件、索引器或這四種成員類型的任何組合構成。接口不能包含字段。接口成員默認是公共的,抽象的,虛擬的。若要實現接口成員,類中的對應成員必須是公共的、非靜態的,并且與接口成員具有相同的名稱和簽名。下面是interface實現的多態版本
public interface IShape{ void Draw();}public class Rectangle :IShape{ public void Draw() { Console.WriteLine("Drawing a Rectangle"); }}public class Square: IShape{ public void Draw() { Console.WriteLine("Drawing a Square"); }}class Program{ static void Main(string[]args) { System.Collections.Generic.List<IShape>shapes =new List<IShape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(IShape s inshapes) { s.Draw(); } Console.ReadLine(); }}
抽象類與接口
類可以實現無限個接口,但僅能從一個抽象(或任何其他類型)類繼承。從抽象類派生的類仍可實現接口。msdn的在接口和抽象類的選擇方面給的一些建議,
如果預計要創建組件的多個版本,則創建抽象類。抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動更新。另一方面,接口一旦創建就不能更改。如果需要接口的新版本,必須創建一個全新的接口。
如果創建的功能將在大范圍的全異對象間使用,則使用接口。抽象類應主要用于關系密切的對象,而接口最適合為不相關的類提供通用功能。
如果要設計小而簡練的功能塊,則使用接口。如果要設計大的功能單元,則使用抽象類。
如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類。抽象類允許部分實現類,而接口不包含任何成員的實現。
一個綜合性的實例
public interface IShape{ void Draw();}public class Shape:IShape{ void IShape.Draw() { Console.WriteLine("Shape IShape.Draw()"); } public virtual void Draw() { Console.WriteLine("Shape virtual Draw()"); }}public class Rectangle :Shape,IShape{ void IShape.Draw() { Console.WriteLine("Rectangle IShape.Draw()"); } public newvirtual void Draw() { Console.WriteLine("Rectangle virtual Draw()"); }}public class Square :Rectangle{ public override void Draw() { Console.WriteLine("Square override Draw()"); }}class Program{ static void Main(string[]args) { Square squre = new Square(); Rectangle rect = squre; Shape shape = squre; IShape ishape = squre; squre.Draw(); rect.Draw(); shape.Draw(); ishape.Draw(); Console.ReadLine(); }}/*運行結果:Square override Draw()①Square override Draw()②Shape virtual Draw()③Rectangle IShape.Draw()④*/
在這個程序里,把派生類實例賦給父類變量或者接口。對結果①無需解釋。結果②,因為Draw方法是虛方法,虛方法的調用規則是調用離實例變量最近的override版本方法,Square類中的Draw方法是離實例square最近的方法,即使是把Square類型的實例賦值給Rectangle類型的變量去訪問,仍然調用的是Square類重寫的方法。對于結果③,也是虛方法調用,在子類Rectangle中的draw方法用new修飾,這就表明shape類中的virtual到此中斷,后面Square中的override版是針對Rectangle中的Draw方法,此時,離square實例最近的實現就是Shape類中的Draw 方法,因為Shape類中的Draw方法沒有override的版本只能調用本身的virtual版了。結果④,因為Rectangle重新聲明實現接口IShape,接口調用同樣符合虛方法調用規則,調用離它最近的實現,Rectangle中的實現比Shape中的實現離實例square更近。Rectangle中的IShape.Draw()方法是顯式接口方法實現,對于它不能有任何的訪問修飾符,只能通過接口變量訪問它,同時也不能用virtual或者override進行修飾,也不能被派生類型調用。只能用IShape變量進行訪問。如果類型中有顯式接口的實現,而且用的是接口變量,默認調用顯式接口的實現方法。
override和方法選擇
public class Base{ public virtual void Write(int num) { Console.WriteLine("int:" + num.ToString()); }}public class Derived :Base{ public override void Write(int num) { Console.WriteLine("derived:" + num.ToString()); } public void Write(double num) { Console.WriteLine("derived double:" + num.ToString()); }}
希望本文所述對大家的C#程序設計有所幫助。
新聞熱點
疑難解答