下面是自己寫的一個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窗體可以用抽象類來設計,可以把公有操作和屬性放到一個抽象類里,讓窗體和對話框繼承自這個抽象類,再根據自己的需求進行擴展和完善。
打印操作可以作為一個接口提供給每個需要此功能的窗體,因為窗體的內容不同,就要根據他們自己的要求去實現自己的打印功能。打印時只通過接口來調用,而不用在乎是那個窗體要打印。
