轉載自 http://hi.baidu.com/sjjqaa2010/blog/item/963bd13e0cdcadf33c6d974a.html
一、抽象類別
(1) 抽象方法只作聲明,而不包含實現,可以看成是沒有實現體的虛方法
(2) 抽象類別不能被執行個體化
(3) 抽象類別可以但不是必須有抽象屬性和抽象方法,但是一旦有了抽象方法,就一定要把這個類聲明為抽象類別
(4) 具體衍生類別必須覆蓋基類的抽象方法
(5) 抽象衍生類別可以覆蓋基類的抽象方法,也可以不覆蓋。如果不覆蓋,則其具體衍生類別必須覆蓋它們。如:
using System;
public abstract class A //抽象類別A
{
private int num=0;
public int Num //抽象類別包含屬性
{
get
{
return num;
}
set
{
num = value;
}
}
public virtual int getNum() //抽象類別包含虛方法
{
return num;
}
public void setNum(int n) // //抽象類別包含普通方法
{
this.num = n;
}
public abstract void E(); //類A中的抽象方法E
}
public abstract class B : A //由於類B繼承了類A中的抽象方法E,所以類B也變成了抽象類別
{
}
public class C : B
{
public override void E() //重寫從類A繼承的抽象方法。如果類B自己還定義了抽象方法,也必須重寫
{
//throw new Exception("The method or operation is not implemented.");
}
}
public class Test
{
static void Main()
{
C c = new C();
c.E();
}
}
二、接 口
(1) 介面不能被執行個體化
(2) 介面只能包含方法聲明
(3) 介面的成員包括方法、屬性、索引器、事件
(4) 介面中不能包含常量、欄位(域)、建構函式、解構函式、靜態成員。如:
public delegate void EventHandler(object sender, Event e);
public interface ITest
{
//int x = 0;
int A
{
get;
set;
}
void Test();
event EventHandler Event;
int this[int index]
{
get;
set;
}
}
(5) 介面中的所有成員預設為public,因此介面中不能有private修飾符
(6) 衍生類別必須實現介面的所有成員
(7) 一個類可以直接實現多個介面,介面之間用逗號隔開
(8) 一個介面可以有多個父介面,實現該介面的類必須實現所有父介面中的所有成員
三、抽象類別和介面
相同點:
(1) 都可以被繼承
(2) 都不能被執行個體化
(3) 都可以包含方法聲明
(4) 衍生類別必須實現未實現的方法
區 別:
(1) 抽象基類可以定義欄位、屬性、方法實現。介面只能定義屬性、索引器、事件、和方法聲明,不能包含欄位。抽象類別可以定義變數,介面不行。
(2) 抽象類別是一個不完整的類,需要進一步細化,而介面是一個行為規範。微軟的自訂介面總是後帶able欄位,證明其是表述一類“我能做。。。”
(3) 介面可以被多重實現,抽象類別只能被單一繼承
(4) 抽象類別更多的是定義在一系列緊密相關的類間,而介面大多數是關係疏鬆但都實現某一功能的類中
(5) 抽象類別是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;介面是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性
(6) 介面基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法
(7) 介面可以用於支援回調,而繼承並不具備這個特點
(8) 抽象類別實現的具體方法預設為虛的,但實現介面的類中的介面方法卻預設為非虛的,當然您也可以聲明為虛的
(9) 如果抽象類別實現介面,則可以把介面中方法映射到抽象類別中作為抽象方法而不必實現,而在抽象類別的子類中實現介面中方法
使用規則:
1、抽象類別主要用於關係密切的對象,而介面最適合為不相關的類提供通用功能
2、如果要設計大的功能單元,則使用抽象類別;如果要設計小而簡練的功能塊,則使用介面。
3、如果預計要建立組件的多個版本,則建立抽象類別。介面一旦建立就不能更改。如果需要介面的新版本,必須建立一個全新的介面。
4、如果建立的功能將在大範圍的全異對象間使用,則使用介面;如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類別。
5、分析對象,提煉內部共性形成抽象類別,用以表示對象本質,即“是什麼”。為外部提供調用或功能需要擴充時優先使用介面
6、好的介面定義應該是具有專一功能性的,而不是多功能的,否則造成介面汙染。如果一個類只是實現了這個介面的中一個功能,而不得不去實現介面中的其他方法,就叫介面汙染
7、盡量避免使用繼承來實現組建功能,而是使用黑箱複用,即對象組合。因為繼承的層次增多,造成最直接的後果就是當你調用這個類群中某一類,就必須把他們全部載入到棧中!後果可想而知。(結合堆棧原理理解)。同時,有心的朋友可以留意到微軟在構建一個類時,很多時候用到了對象組合的方法。比如 asp.net中,Page類,有Server Request等屬性,但其實他們都是某個類的對象。使用Page類的這個對象來調用另外的類的方法和屬性,這個是非常基本的一個設計原則
例 如:
Window表單可以用抽象類別來設計,可以把公有操作和屬性放到一個抽象類別裡,讓表單和對話方塊繼承自這個抽象類別,再根據自己的需求進行擴充和完善。
列印操作可以作為一個介面提供給每個需要此功能的表單,因為表單的內容不同,就要根據他們自己的要求去實現自己的列印功能。列印時只通過介面來調用,而不用在乎是那個表單要列印。
四、其它文章
共性、個性與選擇
有的書上寫到C#推薦使用介面(Interface)來替代抽象基類(Abstract Class),並強調使用介面的諸多好處,這點我不敢苟同,從上面列表中看來,兩者之間還是存在不少差異的,而這種差異的存在性必然決定了適用情境的不同,例如在抽象基類中可以為部分方法提供預設的實現,從而避免在子類中重複實現它們,提高代碼的可重用性,這是抽象類別的優勢所在;而介面中只能包含抽象方法。至於何時使用抽象基類何時使用介面關鍵還是取決於使用者是如何看待繼承類之間的聯絡的,使用者更加關心的是它們之間的個性差異還是它們之間的共性聯絡。舉個生活中的例子加以說明。
如果給你三個對象分別是人、魚、青蛙,讓你為他們設計個基類來概括它們之間的聯絡,那麼首先給你的感覺肯定是它們個體間的差異性較大,很難抽象出共性,然而若讓你概括他們行為之間的共性,你可能想了想會意識到他們都會遊泳,只不過是遊泳方式迥異。那麼這時你就應當考慮使用介面而不是抽象基類,原因有三條:
1. 個性大於共性。
2. 差異較大的個性間具有某些相同的行為。
3. 相同行為的實現方式有較大區別。
這時再給你三個對象,分別是鯽魚、鯉魚、金魚,仍然讓你設計基類來概括它們之間的聯絡,那麼你第一個意識到的肯定是它們都屬於魚類,其次是他們遊泳的方式可能稍有差異,這時就應當使用抽象基類而不是介面,對比著上面的例子,原因也有三條:
interface ISwim
{
void Swim();
}
public class Person : ISwim
{
public void Swim()
{
//Swimming in person's style.
}
}
public class Frog : ISwim
{
public void Swim()
{
//Swimming in frog's style.
}
}
public class Fish : ISwim
{
public void Swim()
{
//Swimming in fish's style.
}
}
1. 共性大於個性
2. 共性相同的個體間必然具有相同的屬性與行為
3. 相同行為的實現方式具有一定區別
abstract public class Fish
{
abstract public void Swim();
}
public class 鯽魚 : Fish
{
public override void Swim()
{
//Swim like a 鯽魚
}
}
public class 鯉魚 : Fish
{
public override void Swim()
{
//Swim like a 鯉魚
}
}
public class 金魚 : Fish
{
public override void Swim()
{
//Swim like a 金魚
}
}
觀察在使用介面或是使用抽象基類的幾條理由中,第三條理由其實是一樣的,它所描述的是物件導向中多態的概念,即通過覆蓋父類的方法來實現,在運行時根據傳遞的對象引用,來調用相應的方法。第二條理由開始產生分歧,介面更加強調了繼承對象間具有相同的行為,而抽象類別同時還強調了繼承對象間具有相同的屬性。而真正將介面與抽象基類區分開的則是理由
一,歸納如下:
當在差異較大的對象間尋求功能上的共性時,使用介面。
當在共性較多的對象間尋求功能上的差異時,使用抽象基類。
通過相同與不同的比較,我們只能說介面和抽象類別,各有所長,但無優略。在實際的編程實踐中,我們要視具體情況來酌情量才,但是以下的經驗和積累,或許能給大家一些啟示,除了我的一些積累之外,很多都來源於經典,我相信經得起考驗。所以在規則與場合中,我們學習這些經典,最重要的是學以致用,當然我將以一家之言博大家之笑,看官請繼續。
規則與場合
1.請記住,物件導向思想的一個最重要的原則就是:面向介面編程。
2.藉助介面和抽象類別,23個設計模式中的很多思想被巧妙的實現了,我認為其精髓簡單說來就是:面向抽象編程。
3.抽象類別應主要用於關係密切的對象,而介面最適合為不相關的類提供通用功能。
4.介面著重於CAN-DO關聯類型,而抽象類別則偏重於IS-A式的關係;
5.介面多定義對象的行為;抽象類別多定義對象的屬性;
6.介面定義可以使用public、protected、internal 和private修飾符,但是幾乎所有的介面都定義為public,原因就不必多說了。
7.“介面不變”,是應該考慮的重要因素。所以,在由介面增加擴充時,應該增加新的介面,而不能更改現有介面。
8.盡量將介面設計成功能單一的功能塊,以.NET Framework為例,IDisposable、IDisposable、IComparable、IEquatable、IEnumerable等都只包含一個公用方法。
9.介面名稱前面的大寫字母“I”是一個約定,正如欄位名以底線開頭一樣,請堅持這些原則。
10.在介面中,所有的方法都預設為public。
11.如果預計會出現版本問題,可以建立“抽象類別”。例如,建立了狗(Dog)、雞(Chicken)和鴨(Duck),那麼應該考慮抽象出動物(Animal)來應對以後可能出現風馬牛的事情。而向介面中添加新成員則會強制要求修改所有衍生類別,並重新編譯,所以版本式的問題最好以抽象類別來實現。
12.從抽象類別派生的非抽象類別必須包括繼承的所有抽象方法和抽象訪問器的實實現。
13.對抽象類別不能使用new關鍵字,也不能被密封,原因是抽象類別不能被執行個體化。
14.在抽象方法聲明中不能使用 static 或 virtual 修飾符。