下面是自己寫的一個demo,體現抽象類和接口的用法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 抽象類和接口 { /* * 相同點: (1) 都可以被繼承 (2) 都不能被實例化 (3) 都可以包含方法聲明 (4) 派生類必須實現未實現的方法 區 別: (1) 抽象基類可以定義字段、屬性、方法實現。接口只能定義屬性、索引器、事件、和方法聲明,不能包含字段。 (2) 抽象類是一個不完整的類,需要進一步細化,而接口是一個行為規范。微軟的自定義接口總是后帶able字段,證明其是表述一類“我能做。。。” (3) 接口可以被多重實現,抽象類只能被單一繼承 (4) 抽象類更多的是定義在一系列緊密相關的類間,而接口大多數是關系疏松但都實現某一功能的類中 (5) 抽象類是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;接口是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性 (6) 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法 (7) 接口可以用於支持回調,而繼承並不具備這個特點 (8) 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的 (9) 如果抽象類實現接口,則可以把接口中方法映射到抽象類中作為抽象方法而不必實現,而在抽象類的子類中實現接口中方法 */ public delegate void EnableHandler(object sender, EventArgs e); public delegate void CPU_CallBack(CPU cpu); public interface i7_CPU_Interface { void LGA2011(); bool Enable { get; } int this[int index] { get; } event EventHandler EnableLGA2011; } public abstract class CPU : i7_CPU_Interface { public event EventHandler EnableLGA2011; int[] pins; string _name; double _frequency; int _kernel; int _pinNum; bool _isEnableLGA2011; public CPU(string name, double frequency, int kernel, int pin, bool isEnableLGA2011) { _name = name; _frequency = frequency; _kernel = kernel; _pinNum = pin; pins=new int[pin]; _isEnableLGA2011 = isEnableLGA2011; EnableLGA2011 += new EventHandler(CPU_EnableLGA2011); } void CPU_EnableLGA2011(object sender, EventArgs e) { Console.WriteLine("我支持LGA2011"); } public string Name { get { return _name; } } public double Frequency { get { return _frequency; } } public int Kernel { get { return _kernel; } } public int PinNum { get { return _pinNum; } } protected abstract void PowerOn(); protected abstract void SelfCheck(); public override string ToString() { return string.Format("我是{0} CPU,我的主頻是{1:0.0},我有{2}個內核,我的引腳有{3}個。", _name, _frequency, _kernel, _pinNum); } public void LGA2011() { Console.WriteLine("1、處理器最高可達八核。"); Console.WriteLine("2、支持四通道DDR3內存。"); Console.WriteLine("3、支持PCI-E 3.0規范。"); Console.WriteLine("4、芯片組使用單芯片設計,支持兩個SATA 3Gbps和多達十個SATA/SAS 6Gbps接口。"); } public bool Enable { get { if (_isEnableLGA2011) { EnableLGA2011.Invoke(this, null); } return _isEnableLGA2011; } } public int this[int index] { get { if (index < pins.Length) return pins[index]; return -1; } } } public class i7_4770k : CPU { public i7_4770k() : base("i7-4770k", 3.6, 8, 2011,true) { Console.WriteLine(base.ToString()); PowerOn(); SelfCheck(); } protected override void PowerOn() { Console.WriteLine("i7-4770k 上電完成"); } protected override void SelfCheck() { Console.WriteLine("i7-4770k 自檢完成"); } } public class i7_4790k : CPU { public i7_4790k() : base("i7-4790k", 4.0, 8, 2011, true) { Console.WriteLine(base.ToString()); PowerOn(); SelfCheck(); } protected override void PowerOn() { Console.WriteLine("i7-4790k 上電完成"); } protected override void SelfCheck() { Console.WriteLine("i7-4790k 自檢完成"); } } public class CreatCPU { CPU_CallBack callback; CPU _cpu = null; public CreatCPU(CPU cpu) { _cpu = cpu; //接口回調,任何實現了該接口的對象都可以被CreatCPU類對象所回調,保證了代碼對環境的適應性 callback = new CPU_CallBack(cpu_call_back); } void cpu_call_back(CPU u) { if (u.Enable) u.LGA2011(); } public void ShowMessage() { callback.Invoke(_cpu); } } class Program { static void Main(string[] args) { CreatCPU creatI7_4770k = new CreatCPU(new i7_4770k()); creatI7_4770k.ShowMessage(); Console.WriteLine(); CreatCPU creatI7_4790k = new CreatCPU(new i7_4790k()); creatI7_4790k.ShowMessage(); Console.Read(); } } }
用了幾年C#,總結一下抽象類和接口的相同點與不同點。看了別人寫的感覺很全面:
相同點:
(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窗體可以用抽象類來設計,可以把公有操作和屬性放到一個抽象類里,讓窗體和對話框繼承自這個抽象類,再根據自己的需求進行擴展和完善。
打印操作可以作為一個接口提供給每個需要此功能的窗體,因為窗體的內容不同,就要根據他們自己的要求去實現自己的打印功能。打印時只通過接口來調用,而不用在乎是那個窗體要打印。