C#中多態(tài)現(xiàn)象和多態(tài)的實(shí)現(xiàn)方法
本文實(shí)例講述了C#中多態(tài)現(xiàn)象和多態(tài)的實(shí)現(xiàn)方法。分享給大家供大家參考。具體分析如下:
面向?qū)ο蟮奶卣鞣庋b、繼承和多態(tài)。Polymorphism(多態(tài)性)來源于希臘單詞,指“多種形態(tài)”。多態(tài)性的一個(gè)重要特征是方法的調(diào)用是在運(yùn)行時(shí)確定而不是編譯時(shí)。在.NET中用于實(shí)現(xiàn)多態(tài)性的關(guān)鍵詞有virtual、override、abstract、interface。
一、virtual實(shí)現(xiàn)多態(tài)
shape類是通用的基類,draw是一個(gè)虛方法,每個(gè)派生類都可以有自己的override版本,在運(yùn)行時(shí)可以用shape類的變量動(dòng)態(tài)的調(diào)用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();
/*運(yùn)行結(jié)果
Drawing a Rectangle
Drawing a Square
Drawing a Rectangle
*/
}
}
方法、屬性、事件、索引器都可以被virtual修飾,但是字段不可以。派生類必須用override表示類成員參與虛調(diào)用。假如把Square中的draw方法替換為用new 修飾,則表示draw方法不參與虛調(diào)用,而且是一個(gè)新的方法,只是名字和基類方法重名。
public new void Draw()
{
Console.WriteLine("Drawing a Square");
base.Draw();
}
這個(gè)方法在Main方法中的foreach中將不會(huì)被調(diào)用,它不是虛方法了。用new修飾符后的程序運(yùn)行結(jié)果,
/*運(yùn)行結(jié)果 Drawing a Rectangle Drawing a Rectangle */
假如說虛方法在rectangle擴(kuò)展后,不希望square擴(kuò)展了,可以在方法前加上sealed修飾符,
如下
public class Rectangle :Shape
{
public sealed override voidDraw()
{
Console.WriteLine("Drawing a Rectangle");
}
}
當(dāng)派生類重寫某個(gè)虛擬成員時(shí),即使該派生類的實(shí)例被當(dāng)作基類的實(shí)例訪問或者把派生類實(shí)例賦給父類變量進(jìn)行訪問,但是還是會(huì)調(diào)用派生類重寫后的成員,可以把代碼改為如下形式,
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();
/*運(yùn)行結(jié)果
Drawing a Rectangle
Drawing a Square
Drawing a Rectangle
*/
}
二、abstract實(shí)現(xiàn)多態(tài)
被abstract修飾的方法,默認(rèn)是虛擬的,但是不能出現(xiàn)virtual關(guān)鍵詞修飾。被abstract修飾的類可以有已實(shí)現(xiàn)的成員,可以有自己的字段,可以有非abstract 修飾的方法,但是不能實(shí)例化因?yàn)槌橄蟮臇|西是沒有實(shí)例對應(yīng)的。比如,有人讓我們畫個(gè)圖形(抽象)是畫不出來的,但是讓畫個(gè)矩形(具體)是可以畫出來的。下面是用abstract實(shí)現(xiàn)的多態(tài)版本,
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關(guān)鍵詞進(jìn)行擴(kuò)展。同樣可以用關(guān)鍵詞sealed阻止派生類進(jìn)行擴(kuò)展。
interface實(shí)現(xiàn)多態(tài)
接口可由方法、屬性、事件、索引器或這四種成員類型的任何組合構(gòu)成。接口不能包含字段。接口成員默認(rèn)是公共的,抽象的,虛擬的。若要實(shí)現(xiàn)接口成員,類中的對應(yīng)成員必須是公共的、非靜態(tài)的,并且與接口成員具有相同的名稱和簽名。下面是interface實(shí)現(xiàn)的多態(tài)版本
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();
}
}
抽象類與接口
類可以實(shí)現(xiàn)無限個(gè)接口,但僅能從一個(gè)抽象(或任何其他類型)類繼承。從抽象類派生的類仍可實(shí)現(xiàn)接口。msdn的在接口和抽象類的選擇方面給的一些建議,
如果預(yù)計(jì)要?jiǎng)?chuàng)建組件的多個(gè)版本,則創(chuàng)建抽象類。抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動(dòng)更新。另一方面,接口一旦創(chuàng)建就不能更改。如果需要接口的新版本,必須創(chuàng)建一個(gè)全新的接口。
如果創(chuàng)建的功能將在大范圍的全異對象間使用,則使用接口。抽象類應(yīng)主要用于關(guān)系密切的對象,而接口最適合為不相關(guān)的類提供通用功能。
如果要設(shè)計(jì)小而簡練的功能塊,則使用接口。如果要設(shè)計(jì)大的功能單元,則使用抽象類。
如果要在組件的所有實(shí)現(xiàn)間提供通用的已實(shí)現(xiàn)功能,則使用抽象類。抽象類允許部分實(shí)現(xiàn)類,而接口不包含任何成員的實(shí)現(xiàn)。
一個(gè)綜合性的實(shí)例
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();
}
}
/*運(yùn)行結(jié)果:
Square override Draw()①
Square override Draw()②
Shape virtual Draw()③
Rectangle IShape.Draw()④
*/
在這個(gè)程序里,把派生類實(shí)例賦給父類變量或者接口。對結(jié)果①無需解釋。結(jié)果②,因?yàn)镈raw方法是虛方法,虛方法的調(diào)用規(guī)則是調(diào)用離實(shí)例變量最近的override版本方法,Square類中的Draw方法是離實(shí)例square最近的方法,即使是把Square類型的實(shí)例賦值給Rectangle類型的變量去訪問,仍然調(diào)用的是Square類重寫的方法。對于結(jié)果③,也是虛方法調(diào)用,在子類Rectangle中的draw方法用new修飾,這就表明shape類中的virtual到此中斷,后面Square中的override版是針對Rectangle中的Draw方法,此時(shí),離square實(shí)例最近的實(shí)現(xiàn)就是Shape類中的Draw 方法,因?yàn)镾hape類中的Draw方法沒有override的版本只能調(diào)用本身的virtual版了。結(jié)果④,因?yàn)镽ectangle重新聲明實(shí)現(xiàn)接口IShape,接口調(diào)用同樣符合虛方法調(diào)用規(guī)則,調(diào)用離它最近的實(shí)現(xiàn),Rectangle中的實(shí)現(xiàn)比Shape中的實(shí)現(xiàn)離實(shí)例square更近。Rectangle中的IShape.Draw()方法是顯式接口方法實(shí)現(xiàn),對于它不能有任何的訪問修飾符,只能通過接口變量訪問它,同時(shí)也不能用virtual或者override進(jìn)行修飾,也不能被派生類型調(diào)用。只能用IShape變量進(jìn)行訪問。如果類型中有顯式接口的實(shí)現(xiàn),而且用的是接口變量,默認(rèn)調(diào)用顯式接口的實(shí)現(xiàn)方法。
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#程序設(shè)計(jì)有所幫助。
相關(guān)文章
C#使用ODBC與OLEDB連接數(shù)據(jù)庫的方法示例
這篇文章主要介紹了C#使用ODBC與OLEDB連接數(shù)據(jù)庫的方法,結(jié)合實(shí)例形式分析了C#基于ODBC與OLEDB實(shí)現(xiàn)數(shù)據(jù)庫連接操作簡單操作技巧,需要的朋友可以參考下2017-05-05
C#.NET采用HTML模板發(fā)送電子郵件完整實(shí)例
這篇文章主要介紹了C#.NET采用HTML模板發(fā)送電子郵件的方法,主要包括了HTML模板、替換函數(shù)與郵件函數(shù)三部分,是非常實(shí)用的功能,需要的朋友可以參考下2014-09-09
C#中List轉(zhuǎn)IList的實(shí)現(xiàn)
本文主要介紹了C#中List轉(zhuǎn)IList的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
C# 關(guān)于AppDomain的一些總結(jié)
這篇文章主要介紹了C# 關(guān)于AppDomain的一些總結(jié),幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2021-02-02
C# Onnx實(shí)現(xiàn)輕量實(shí)時(shí)的M-LSD直線檢測
這篇文章主要為大家詳細(xì)介紹了C#如何結(jié)合Onnx實(shí)現(xiàn)輕量實(shí)時(shí)的M-LSD直線檢測,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11
C#中WPF內(nèi)存回收與釋放LierdaCracker的實(shí)現(xiàn)
本文主要介紹了C#中WPF內(nèi)存回收與釋放LierdaCracker的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

