C#中的多態(tài)深入理解
封裝、繼承、多態(tài),面向?qū)ο蟮娜筇匦?,前兩?xiàng)理解相對(duì)容易,但要理解多態(tài),特別是深入的了解,對(duì)于初學(xué)者而言可能就會(huì)有一定困難了。我一直認(rèn)為學(xué)習(xí)OO的最好方法就是結(jié)合實(shí)踐,封裝、繼承在實(shí)際工作中的應(yīng)用隨處可見(jiàn),但多態(tài)呢?也許未必,可能不經(jīng)意間用到也不會(huì)把它跟“多態(tài)”這個(gè)詞對(duì)應(yīng)起來(lái)。在此拋磚引玉,大家討論,個(gè)人能力有限,不足之處還請(qǐng)指正。
之前看到過(guò)類(lèi)似的問(wèn)題:如果面試時(shí)主考官要求你用一句話來(lái)描述多態(tài),盡可能的精煉,你會(huì)怎么回答?當(dāng)然答案有很多,每個(gè)人的理解和表達(dá)不盡相同,但我比較趨向這樣描述:通過(guò)繼承實(shí)現(xiàn)的不同對(duì)象調(diào)用相同的方法,表現(xiàn)出不同的行為,稱(chēng)之為多態(tài)。
例1:
public class Animal
{
public virtual void Eat()
{
Console.WriteLine("Animal eat");
}
}
public class Cat : Animal
{
public override void Eat()
{
Console.WriteLine("Cat eat");
}
}
public class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Dog eat");
}
}
class Tester
{
static void Main(string[] args)
{
Animal[] animals = new Animal[3];
animals[0] = new Animal();
animals[1] = new Cat();
animals[2] = new Dog();
for (int i = 0; i < 3; i++)
{
animals[i].Eat();
}
}
}
輸出如下:
Animal eat...
Cat eat...
Dog eat...
在上面的例子中,通過(guò)繼承,使得Animal對(duì)象數(shù)組中的不同的對(duì)象,在調(diào)用Eat()方法時(shí),表現(xiàn)出了不同的行為。
多態(tài)的實(shí)現(xiàn)看起來(lái)很簡(jiǎn)單,要完全理解及靈活的運(yùn)用c#的多態(tài)機(jī)制,也不是一件容易的事,有很多需要注意的地方。
1. new的用法
先看下面的例子。
例2:
public class Animal
{
public virtual void Eat()
{
Console.WriteLine("Animal eat");
}
}
public class Cat : Animal
{
public new void Eat()
{
Console.WriteLine("Cat eat");
}
}
class Tester
{
static void Main(string[] args)
{
Animal a = new Animal();
a.Eat();
Animal ac = new Cat();
ac.Eat();
Cat c = new Cat();
c.Eat();
}
}
運(yùn)行結(jié)果為:
Animal eat...
Animal eat...
Cat eat...
可以看出,當(dāng)派生類(lèi)Cat的Eat()方法使用new修飾時(shí),Cat的對(duì)象轉(zhuǎn)換為Animal對(duì)象后,調(diào)用的是Animal類(lèi)中的Eat()方法。其實(shí)可以理解為,使用new關(guān)鍵字后,使得Cat中的Eat()方法和Animal中的Eat()方法成為毫不相關(guān)的兩個(gè)方法,只是它們的名字碰巧相同而已。所以, Animal類(lèi)中的Eat()方法不管用還是不用virtual修飾,也不管訪問(wèn)權(quán)限如何,或者是沒(méi)有,都不會(huì)對(duì)Cat的Eat()方法產(chǎn)生什么影響(只是因?yàn)槭褂昧薾ew關(guān)鍵字,如果Cat類(lèi)沒(méi)用從Animal類(lèi)繼承Eat()方法,編譯器會(huì)輸出警告)。
我想這是設(shè)計(jì)者有意這么設(shè)計(jì)的,因?yàn)橛袝r(shí)候我們就是要達(dá)到這種效果。嚴(yán)格的說(shuō),不能說(shuō)通過(guò)使用new來(lái)實(shí)現(xiàn)多態(tài),只能說(shuō)在某些特定的時(shí)候碰巧實(shí)現(xiàn)了多態(tài)的效果。
2.override實(shí)現(xiàn)多態(tài)
真正的多態(tài)使用override來(lái)實(shí)現(xiàn)的?;剡^(guò)去看前面的例1,在基類(lèi)Animal中將方法Eat()用virtual標(biāo)記為虛擬方法,再在派生類(lèi)Cat和Dog中用override對(duì)Eat()修飾,進(jìn)行重寫(xiě),很簡(jiǎn)單就實(shí)現(xiàn)了多態(tài)。需要注意的是,要對(duì)一個(gè)類(lèi)中一個(gè)方法用override修飾,該類(lèi)必須從父類(lèi)中繼承了一個(gè)對(duì)應(yīng)的用virtual修飾的虛擬方法,否則編譯器將報(bào)錯(cuò)。
好像講得差不多了,還有一個(gè)問(wèn)題,不知道你想沒(méi)有。就是多層繼承中又是怎樣實(shí)現(xiàn)多態(tài)的。比如類(lèi)A是基類(lèi),有一個(gè)虛擬方法method()(virtual修飾),類(lèi)B繼承自類(lèi)A,并對(duì)method()進(jìn)行重寫(xiě)(override修飾),現(xiàn)在類(lèi)C又繼承自類(lèi)B,是不是可以繼續(xù)對(duì)method()進(jìn)行重寫(xiě),并實(shí)現(xiàn)多態(tài)呢?看下面的例子。
例3:
public class Animal
{
public virtual void Eat()
{
Console.WriteLine("Animal eat");
}
}
public class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Dog eat");
}
}
public class WolfDog : Dog
{
public override void Eat()
{
Console.WriteLine("WolfDog eat");
}
}
class Tester
{
static void Main(string[] args)
{
Animal[] animals = new Animal[3];
animals[0] = new Animal();
animals[1] = new Dog();
animals[2] = new WolfDog();
for (int i = 0; i < 3; i++)
{
animals[i].Eat();
}
}
}
運(yùn)行結(jié)果為:
Animal eat...
Dog eat...
WolfDog eat...
在上面的例子中類(lèi)Dog繼承自類(lèi)Animal,對(duì)方法Eat()進(jìn)行了重寫(xiě),類(lèi)WolfDog又繼承自Dog,再一次對(duì)Eat()方法進(jìn)行了重寫(xiě),并很好地實(shí)現(xiàn)了多態(tài)。不管繼承了多少層,都可以在子類(lèi)中對(duì)父類(lèi)中已經(jīng)重寫(xiě)的方法繼續(xù)進(jìn)行重寫(xiě),即如果父類(lèi)方法用override修飾,如果子類(lèi)繼承了該方法,也可以用override修飾,多層繼承中的多態(tài)就是這樣實(shí)現(xiàn)的。要想終止這種重寫(xiě),只需重寫(xiě)方法時(shí)用sealed關(guān)鍵字進(jìn)行修飾即可。
3. abstract-override實(shí)現(xiàn)多態(tài)
先在我們?cè)趤?lái)討論一下用abstract修飾的抽象方法。抽象方法只是對(duì)方法進(jìn)行了定義,而沒(méi)有實(shí)現(xiàn),如果一個(gè)類(lèi)包含了抽象方法,那么該類(lèi)也必須用abstract聲明為抽象類(lèi),一個(gè)抽象類(lèi)是不能被實(shí)例化的。對(duì)于類(lèi)中的抽象方法,可以再其派生類(lèi)中用override進(jìn)行重寫(xiě),如果不重寫(xiě),其派生類(lèi)也要被聲明為抽象類(lèi)??聪旅娴睦印?/P>
例4:
public abstract class Animal
{
public abstract void Eat();
}
public class Cat : Animal
{
public override void Eat()
{
Console.WriteLine("Cat eat");
}
}
public class Dog : Animal
{
public override void Eat()
{
Console.WriteLine("Dog eat");
}
}
public class WolfDog : Dog
{
public override void Eat()
{
Console.WriteLine("Wolfdog eat");
}
}
class Tester
{
static void Main(string[] args)
{
Animal[] animals = new Animal[3];
animals[0] = new Cat();
animals[1] = new Dog();
animals[2] = new WolfDog();
for (int i = 0; i < animals.Length; i++)
{
animals[i].Eat();
}
}
}
運(yùn)行結(jié)果為:
Cat eat...
Dog eat...
Wolfdog eat...
從上面可以看出,通過(guò)使用abstract-override可以和virtual-override一樣地實(shí)現(xiàn)多態(tài),包括多層繼承也是一樣的。不同之處在于,包含虛擬方法的類(lèi)可以被實(shí)例化,而包含抽象方法的類(lèi)不能被實(shí)例化。
以上就是我對(duì)c#中多態(tài)的一些淺薄的認(rèn)識(shí),如有錯(cuò)誤的地方,歡迎批評(píng)指正!
相關(guān)文章
C# OpenCvSharp實(shí)現(xiàn)去除字母后面的雜線
這篇文章主要為大家詳細(xì)介紹了C#如何使用OpenCvSharp實(shí)現(xiàn)去除字母后面的雜線效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11C#?winform跨線程操作控件的實(shí)現(xiàn)
本文主要介紹了C#?winform跨線程操作控件的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06C#從數(shù)據(jù)庫(kù)讀取圖片并保存的兩種方法
這篇文章主要介紹了C#從數(shù)據(jù)庫(kù)讀取圖片并保存的方法,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2021-01-01DevExpress設(shè)置TreeList圖片節(jié)點(diǎn)背景色的方法
這篇文章主要介紹了DevExpress設(shè)置TreeList圖片節(jié)點(diǎn)背景色的方法,需要的朋友可以參考下2014-08-08Unity游戲開(kāi)發(fā)之炸彈人游戲的實(shí)現(xiàn)
大家小時(shí)候肯定玩過(guò)這款游戲,炸彈人也算是經(jīng)典中的經(jīng)典啦。本文將利用Unity模擬實(shí)現(xiàn)這一經(jīng)典游戲,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-03-03Unity UGUI的ScrollRect滾動(dòng)視圖組件使用詳解
這篇文章主要為大家介紹了Unity UGUI的ScrollRect滾動(dòng)視圖組件使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07