對于接口來說,C#是有規定使用Interface關鍵字來聲明接口。它的聲明是和類一緻的。可以說接口就是一個特殊的抽象類。如下代碼:
class Program
{
static void Main(string[] args)
{
}
}
//聲明一個可以飛的接口
interface IRunable
{
//包含可以被繼承的子類實作的方法
void Run();
}
由以前的抽象類的知識可以知道,抽象類是沒有辦法執行個體化的(因為含有抽象成員,而抽象成員不含有方法體)。那麼接口可不可以執行個體化呢?答案是肯定的,不能執行個體化。看下面的一段代碼:
這個時候編譯器告訴我們無法建立抽象類或者接口的執行個體。
二,接口可以定義哪些成員
1)接口就是一個定義“具有某種能力的抽象類”,既然接口是類,那麼它的内部可以定義哪些成員呢?
首先,在普通的類中,可以有字段,屬性,方法,索引器,抽象方法等等。那麼接口呢?
看下面直接聲明字段,編譯器會報錯,告訴我們接口内不能聲明字段
既然接口内不能有字段,那也就不存在封裝字段了。是以上邊圖示的封裝字段的代碼也是錯誤的。
同理由上面的代碼也可以知道,在接口中是不可以定義顯式的屬性(因為在屬性中要操作字段指派,但是字段沒有辦法在接口中聲明)。
那麼接口可以聲明自動屬性麼?看下面的代碼:
//聲明一個可以飛的接口
interface IRunable
{
//聲明字段
int nAge { get; set; }
string strName { get; set; }
////包含可以被繼承的子類實作的方法
void Run();
}
代碼可以順利編譯通過,那麼是為什麼呢?這就要看.NET的源碼,我把源碼編譯後的比較結果如下圖:
抽象方法就不用多了,本來接口就是一個抽象愛類,當然可以定義抽象類,但是不在使用abstract關鍵字,而且方法必須沒有方法體;
2)繼承接口的子類必須實作接口的所有抽象成員。
我們先來看下面的代碼:
//聲明一個接口,其中包含屬性和未實作方法void
interface IRunable
{
string strName { get; set; }
void Run();
}
下面來一個實作類,如下:
class Person:IRunable
{
public void Run()
{
Console.WriteLine("我可以奔跑!");
}
}
這時候,我們編譯,編譯器會告訴我們什麼呢?如下圖:
是以繼承接口的類,必須實作接口的所有抽象成員。
正确的代碼如下:
class Person:IRunable
{
public void Run()
{
Console.WriteLine("我可以奔跑!");
}
public string strName
{
get
{
return strName;
}
set
{
strName = value;
}
}
}
通過以上的代碼可以發現:
①我們的繼承類在實作接口成員的時候不需要使用override關鍵字
②實作接口的時候,必須保持簽名一緻
由前面抽象類的知識我們有沒有這樣的疑問,什麼時候使用抽象類,什麼時候使用接口呢?
總結如下:
①使用抽象類:可以找到父類,并且希望通過父類繼承給子類一些成員
②使用接口:接口就是一個純粹的為了規範實作的類。比如:多個類具有相同的方法,但是卻找不到父類,就可以将方法定義在接口中。讓這些類去實作。
下面糾紛别來看兩端代碼,比較抽象類和接口的異同,首先是抽象類:
class Program
{
static void Main(string[] args)
{
Student s = new Student();
//Student類通過繼承獲得NAge屬性
s.NAge = 10;
s.Eat();
Console.WriteLine("--------Student和Worker類分别通過繼承獲得了父類的非私有成員,實作了父類的抽象方法--------");
Worker w = new Worker();
//Worker類通過繼承獲得NAge屬性
w.NAge = 40;
w.Eat();
Console.ReadKey();
}
}
//定義父類
abstract class Person
{
private int nAge;
public int NAge
{
get { return nAge; }
set { nAge = value; }
}
private void Run()
{
Console.WriteLine("我是父類,我可以跑!");
}
public abstract void Eat();
}
class Student : Person
{
//子類覆寫了父類的抽象方法
public override void Eat()
{
Console.WriteLine("我是子類,我繼承了父類,我可以在學校吃飯!");
}
}
class Worker:Person
{
//同樣Worker也通過繼承獲得了父類的非私有成員
public override void Eat()
{
Console.WriteLine("我是子類,我繼承父類,我可以在工廠吃飯");
}
}
接下來,來看看接口是怎麼規範多個類的實作的。
class Program
{
static void Main(string[] args)
{
Student s = new Student();
s.strName = "國小生";
s.Run();
Console.WriteLine(s.strName);
Console.WriteLine("--------------------");
Worker w = new Worker();
w.strName = "看我能不能渎職";
w.Run();
Console.WriteLine(w.strName);
Console.ReadKey();
}
}
interface IRunable
{
//規範子類必須實作strName屬性
string strName { get; set; }
//規範子類必須實作Run()方法
void Run();
}
class Student:IRunable
{
//這裡是子類的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine("我是國小生,我在學校裡面跑步!");
}
}
class Worker:IRunable
{
string strname;
public string strName
{
get
{
return "勞工";
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine( "我是勞工,我需要在廠區跑!");
}
}
由以上的代碼可不可以發現,接口僅僅在規定一個規範子類的實作,而抽象類可以通過繼承,繼承給子類某些成員。
最後來看一下,接口的顯示實作,我先看接口的普通實作(以上的代碼實作接口的方式都是隐式實作)
interface IRunable
{
//規範子類必須實作strName屬性
string strName { get; set; }
//規範子類必須實作Run()方法
void Run();
}
class Student:IRunable
{
//這裡是子類的字段
string strname;
public string strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
public void Run()
{
Console.WriteLine("我是國小生,我在學校裡面跑步!");
}
}
顯式實作接口
class Student:IRunable
{
//這裡是子類的字段
string strname;
//顯示實作接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
void IRunable.Run()
{
Console.WriteLine("我是國小生,我在學校裡面跑步!");
}
}
顯示的實作接口是為了解決方法名沖突的問題。但是顯示實作接口會出現,在上面的代碼中會出現一個問題,如下圖:
為什麼會這樣呢?
因為顯式實作接口的方法是私有的,不能通過對象變量來調用。那應該怎麼調用呢,看下面的代碼:
class Program
{
static void Main(string[] args)
{
//裡氏替換原則,父類變量指向子類對象,并通過父類變量調用子類方法
IRunable ir = new Student();
ir.Run();
Console.ReadKey();
}
}
interface IRunable
{
//規範子類必須實作strName屬性
string strName { get; set; }
//規範子類必須實作Run()方法
void Run();
}
class Student:IRunable
{
//這裡是子類的字段
string strname;
//顯示實作接口
string IRunable.strName
{
get
{
return strname;
}
set
{
strname = value;
}
}
void IRunable.Run()
{
Console.WriteLine("我是國小生,我在學校裡面跑步!");
}
// Student s = new Student();
}
列印結果如下:
顯式實作接口,這個接口的方法,隻能通過接口變量來調用。
接口導圖總結如下:
轉 原作者 http://blog.csdn.net/yisuowushinian/article/category/1090479
為系統而生,為架構而死,為debug奮鬥一輩子; 吃符号的虧,上大小寫的當,最後死在需求上。