interface用來聲明接口
1.只提供一些方法規約,不提供方法主體. 如:
public interface IPerson
{
void getName();//不包含方法主體
}
2.方法不能用public abstract等修飾,無字段變量,無構造函數。
3.方法可包含參數。 如
public interface IPerson
{
void getAge(string s);
}
一個例子(例1):
public interface IPerson
{
IPerson(); //錯誤
string name; //錯誤
public void getIDcard();//錯誤
void getName(); //right
void getAge(string s); //right
}
實現interface的類
1.與繼承類的格式一致,如 public class Chinese:IPerson{}
2.必須實現 interface 中的各個方法
例2,繼承例1
public class Chinese:IPerson
{
public Chinese(){} //添加構造
public void getName(){} //實現getName()
public void getAge(string s){} //實現getAge()
}
abstract聲明抽象類、抽象方法
1.抽象方法所在類必須為抽象類
2.抽象類不能直接實例化,必須由其派生類實現。
3.抽象方法不包含方法主體,必須由派生類以override方式實現此方法,這點跟interface中的方法類似
如
public abstract class Book
{
public Book()
{
}
public abstract void getPrice(); //抽象方法,不含主體
public virtual void getName() //虛方法,可覆蓋
{
Console.WriteLine("this is a test:virtual getName()");
}
public virtual void getContent() //虛方法,可覆蓋
{
Console.WriteLine("this is a test:virtual getContent()");
}
public void getDate() //一般方法,若在派生類中重寫,須使用new關鍵字
{
Console.WriteLine("this is a test: void getDate()");
}
}
public class JavaBook:Book
{
public override void getPrice() //實現抽象方法,必須實現
{
Console.WriteLine("this is a test:JavaBook override abstract getPrice()");
}
public override void getName() //覆蓋原方法,不是必須的
{
Console.WriteLine("this is a test:JavaBook override virtual getName()");
}
}
測試如下:
public class test
{
public test()
{
JavaBook jbook=new JavaBook();
jbook.getPrice(); //將調用JavaBook中getPrice()
jbook.getName(); //將調用JavaBook中getName()
jbook.getContent(); //將調用Book中getContent()
jbook.getDate(); //將調用Book中getDate()
}
public static void Main()
{
test t=new test();
}
}
virtual標記方法為虛方法
1.可在派生類中以override覆蓋此方法
2.不覆蓋也可由對象調用
3.無此標記的方法(也無其他標記),重寫時需用new隱藏原方法
abstract 與virtual : 方法重寫時都使用 override 關鍵字
接口(interface)
簡單地說接口就是一種對行為的契約或者規范。比如我們一說到“筆”,那麼我們就知道它一定可以用來“書寫”,而不管它是鉛筆還是水筆,不管它是用木制的還是塑料制的。這裡的“筆”就相當於一個契約(接口),它描述了“書寫”這樣一個行為。只要這個對象是“筆”,那麼它就一定能“書寫”(而不管對象具體是什麼類型的東西)。正因為有了“筆”對“書寫”行為的這樣一個約定,所以當我們到商店裡去買鋼筆時,不會再問去售貨小姐“這個東西能不能用來書寫”;也不會在第一次用某種牌子的鉛筆之前還要先看說明書才知道它能不能夠書寫。
而在我們的程序裡,我們可以通過接口(interface)來制定這種契約。只要對象實現這個契約,那麼這個對象就一定具有契約中所規定的行為,而不用去管這個對象到底是什麼。當然你可能會說,那我這個對象表面上假裝實現了這個契約,但在對象裡面卻不具有(實現)契約中所規定的行為(就像生活中的一些偽劣產品,廣告裡說實現了什麼,但實際上都是假的),那麼我們的編譯器這個時候就充當明察秋毫的“質檢人員”,把你的“偽劣”對象都找出來,不讓你編譯通過。所以編譯器是我們使用接口的保證。
先來看看C#裡關於interface的語法:
申明一個接口:
public interface 筆
{
void 書寫(); // 申明了書寫行為,但僅僅是“打了個廣告”而已,沒有實現
}
注意:interface裡申明的方法不需要諸如public、private等訪問修飾符,因為interface裡的方法都默認是public的。(秘密的規范還有什麼意義呢?)
一個類被申明為實現某個接口的格式和繼承很相似:
public class 鋼筆 : 筆
{
public void 書寫()
{
// 具體實現書寫的過程
}
}
所以也會經常說類“繼承”了某個接口。
那麼在程序裡定義接口(interface)到底有用呢?這主要體現在3個方面。
1、當我們定義了自己的類,那麼類的方法(即:行為)就是你自己定義的,你必須要通過其他形式(比如文檔)來告訴其他的使用者,你的類有哪些方法,能實現什麼。那麼有了interface,我們可以在做自己的類之前先定義一個interface,然後讓自己的類來遵守(實現)這個interface,那麼你只需要把這個interface交給使用者就可以了,甚至在你的類還沒有coding完成之前就可以先把interface交給使用者,這樣你的類的coding和使用者coding的過程可以同時進行,從而提高開發速度。
2、在我們的程序裡使用interface則可以獲得更好擴充性和靈活性
來個例子(還是以筆為例):
public interface 筆
{
void書寫();
>
}
public class 鋼筆 : 筆
{
publicvoid書寫()
{
...... // 用鋼筆自己的方式來實現到底怎麼書寫
}
...... // 其他鋼筆的屬性和行為
}
public class 鉛筆 : 筆
{
publicvoid書寫()
{
...... // 用鉛筆自己的方式來實現到底怎麼書寫
}
...... // 其他鉛筆筆的屬性和行為
}
public class 學生
{
// 我們傳入的參數是“筆”這個規范類型,而不是具體的鋼筆類型或鉛筆類型
publicvoid寫作文(筆 tool)
{
......
tool.書寫();
.....
}
}
……
public static main()
{
鋼筆 pen;
鉛筆 pencil; // 申明“鉛筆”的對象
pen = new鋼筆(); // 實例“鋼筆”對象實例
pencil = new鉛筆(); // 實例“鉛筆”對象實例
學生 student = new學生();
// 這裡會有一個自動轉型的過程,
//會自動將pen和pencil向上轉換成“筆”來符合 “寫作文(筆 tool)” 這個方法的參數規定
student.寫作文(pen) // 學生用鋼筆寫作文
student.寫作文(pencil) // 學生用鉛筆寫作文
// 當以後發明出了“激光筆”,就讓用學生用激光筆來寫作文,
//而不用去修改 學生.寫作文(筆 tool) 這個方法了(因為激光筆也會遵守“筆”的規范)。
}
從上面的例子可以看出來,我們的學生.寫作文(筆 tool) 這個方法使用了接口作為參數,這樣只要實現了該接口的類型都可以傳遞進去,而不用管它具體是什麼類型的,這使得我們的程序更容易擴充。同樣凡是接受“筆”作為參數的方法,我們都可以動態的把“鋼筆”或“鉛筆”傳進取(至於是傳“鋼筆”還是“鉛筆”可以有很多技巧,比如用配置文件來標志)來動態的實現不同的效果和功能,這使得我們的程序更加靈活。
3、在類似C#和Java這種單根繼承的語言裡,可以通過使用interface來在一定程度上實現多重繼承的效果。
------------------------------------------------------------------------------------------------------------
最後還是說一說C#裡運行時環境對接口的執行過程:
C#程序在運行中是如何使用接口的,如何訪問接口函數,具體流程如下:
1、當調用一個接口的函數時,系統會去檢查這個接口對應實例是什麼;
2、找到這個實例後,再去找這個實例對應的實例類是什麼(實例類,在虛函數一文裡曾說明過);
3、根據這個實例類去檢查該實例類是否和接口發生了捆綁(看是否實現了該接口,冒號後面就是);
4、好!如果實例類實現了該接口(發生了捆綁),那麼它就在這個實例類中找到接口中所申明的方法的定義,然後執行該方法,然後結束。
5、如果沒找到,它就繼續往父類去找,直到找到第一個和接口捆綁的父類為止
6、找到後,它再檢查這個父類裡該方法是否是被定義為虛函數;
7、如果不是,他馬上就執行這個方法,然後結束;
8、如果是,麻煩了,系統又要從頭來過,去檢查該最開始那個實例類裡有否重載了父類裡的這個虛函數…...(具體過程見上一篇《虛函數》)。
DEMO:
interface I
{
voidFunc();
}
class A : I
{
publicvirtualvoidFunc()
{
Console.WriteLine("Func In A");
}
}
classB : A , I // 注意這裡,用接口來實現類似多重繼承的效果
{
publicvoidFunc()
{
Console.WriteLine("Func In B");
}
}
class C : A
{
publicoverridevoidFunc()
{
Console.WriteLine("Func In C");
}
}
class D : A
{
publicnewvoidFunc()
{
Console.WriteLine("Func In D");
}
}
publicstaticvoidmain()
{
I a = newA() ; //申明了接口a,並馬上和一個類的實例發生關系了
I b = newB() ; //申明了接口b,並馬上和一個類的實例發生關系了
I c = newC() ; //申明了接口c,並馬上和一個類的實例發生關系了
I d = newD() ; //申明了接口d,並馬上和一個類的實例發生關系了
//檢查a的實例類A,發現A和接口I捆綁了,所以執行A的函數Func ,結果: Func In A
a.Func();
// 檢查b的實例類B,發現B和接口I捆綁了,所以執行B的函數Func(而不會去執行父類A的,盡管A也
// 實現I接口),結果: Func In B
b.Func();
// 檢查c的實例類C,發現其沒有和接口I捆綁,系統繼續找它的父類. 發現A和I捆綁了,他就去找
// 函數A,發現A是虛擬函數,系統又從頭來找類的實例C,發現C重載(override)了Func,好了,馬
//上執行該函數. 結果是Func In C
c.Func();
// 檢查d的實例類D,發現其沒有和接口I捆綁,系統繼續找它的父類. 發現A和I捆綁了,他就去找
// 函數A,發現A是虛擬函數,系統又從頭來找類的實例D,但是D裡沒有重載(override)Func(而是
// 用new覆蓋了),所以又會到D的父類裡找,所以還是執行A的Func(),結果是Func In A
d.Func() ;
}