接口
什么是接口
接口是指定一組函數(shù)成員而不實現(xiàn)它們的引用類型扬舒。所以只能類和結(jié)構(gòu)來實現(xiàn)接口。
這種描述比較抽象凫佛,直接來看個示例讲坎。
下例中,Main方法創(chuàng)建并初始化了一個CA類的對象愧薛,并將該對象傳遞給PrintInfo方法晨炕。
class CA
{
public string Name; public int Age;
}
class CB
{
public string First; public string Last; public double PersonsAge;
}
class Program
{
static void PrintInfo(CA item)
{
Console.WriteLine("Name: {0},Age {1}",item.Name,item.Age);
}
static void Main()
{
CA a=new CA(){Name="John Doe",Age=35};
PrintInfo(a);
}
}
只要傳入的是CA類型的對象,PrintInfo就能正常工作毫炉。但如果傳入的是CB瓮栗,就不行了。
現(xiàn)在的代碼不能滿足上面的需求,原因有很多费奸。
- PrintInfo的形參指明了實參必須為CA類型的對象
- CB的結(jié)構(gòu)與CA不同弥激,字段的名稱和類型與CA不一樣
接口解決了這一問題。
- 聲明一個IInfo接口愿阐,包含兩個方法–GetName和GetAge微服,每個方法都返回string
- 類CA和CB各自實現(xiàn)了IInfo接口,并實現(xiàn)了兩個方法
- Main創(chuàng)建了CA和CB的實例缨历,并傳入PrintInfo
- 由于類實例實現(xiàn)了接口以蕴,PrintInfo可以調(diào)用那兩個方法,每個類實例執(zhí)行各自的方法辛孵,就好像是執(zhí)行自己類聲明中的方法
interface IInfo //聲明接口
{
string GetName();
string GetAge();
}
class CA:IInfo //聲明了實現(xiàn)接口的CA類
{
public string Name; public int Age;
public string GetName(){return Name;}
public string GetAge(){return Age.ToString();}
}
class CB:IInfo //聲明了實現(xiàn)接口的CB類
{
public string First;
public string Last;
public double PersonsAge;
public string GetName(){return First+""+Last;}
public string GetAge(){return PersonsAge.ToString();}
}
class Program
{
static void PrintInfo(IInfo item)
{
Console.WriteLine("Name: {0},Age {1}",item.GetName(),item.GetAge());
}
static void Main()
{
var a=new CA(){Name="John Doe",Age=35};
var b=new CB(){First="Jane",Last="Doe",PersonsAge=33};
PrintInfo(a);
PrintInfo(b);
}
}
使用IComparable接口的示例
- 第一行代碼創(chuàng)建了包含5個無序整數(shù)的數(shù)組
- 第二行代碼使用了Array類的靜態(tài)Sort方法來排序元素
- 用foreach循環(huán)輸出它們丛肮,顯式以升序排序的數(shù)字
Array.Sort(myInt);
foreach(var i in myInt)
{
Console.WriteLine("{0}",i);
}
Sort方法在int數(shù)組上工作良好,但如果我們嘗試在自己的類上使用會發(fā)生什么呢魄缚?
class MyClass
{
public int TheValue;
}
...
MyClass[] mc=new MyClass[5];
...
Array.Sort(mc);
運(yùn)行上面的代碼宝与,將會得到一個異常。Sort并不能對MyClass對象數(shù)組排序冶匹,因為它不知道如何比較自定義的對象伴鳖。Array類的Sort方法其實依賴于一個叫做IComparable的接口,它聲明在BCL中徙硅,包含唯一的方法CompareTo。
public interface IComparable
{
int CompareTo(object obj);
}
盡管接口聲明中沒有為CompareTo方法提供實現(xiàn)搞疗,但I(xiàn)Comparable接口的.NET文檔中描述了該方法應(yīng)該做的事情嗓蘑,可以在創(chuàng)建實現(xiàn)該接口的類或結(jié)構(gòu)時參考。
文檔中寫到匿乃,在調(diào)用CompareTo方法時桩皿,它應(yīng)該返回以下幾個值:
- 負(fù)數(shù)值 當(dāng)前對象小于參數(shù)對象
- 整數(shù)值 當(dāng)前對象大于參數(shù)對象
- 零 兩個對象相等
我們可以通過讓類實現(xiàn)IComparable來使Sort方法可以用于MyClass對象。要實現(xiàn)一個接口幢炸,類或結(jié)構(gòu)必須做兩件事情:
- 必須在基類列表后面列出接口名稱
- 必須實現(xiàn)接口的每個成員
例:MyClass中實現(xiàn)了IComparable接口
class MyClass:IComparable
{
public int TheValue;
public int CompareTo(object obj)
{
var mc=(MyClass)obj;
if(this.TheValue<mc.TheValue)return -1;
if(this.TheValue>mc.TheValue)return 1; return 0;
}
}
例:完整示例代碼
class MyClass:IComparable
{
public int TheValue;
public int CompareTo(object obj)
{
var mc=(MyClass)obj;
if(this.TheValue<mc.TheValue)return -1;
if(this.TheValue>mc.TheValue)return 1; return 0;
}
}
class Program
{
static void PrintInfo(string s,MyClass[] mc)
{
Console.WriteLine(s);
foreach(var m in mc)
{
Console.WriteLine("{0}",m.TheValue);
}
Console.WriteLine("");
}
static void Main()
{
var myInt=new[] {20,4,16,9,2};
MyClass[] mcArr=new MyClass[5];
for(int i=0;i<5;i++)
{
mcArr[i]=new MyClass();
mcArr[i].TheValue=myInt[i];
}
PrintOut("Initial Order: ",mcArr);
Array.Sort(mcArr);
PrintOut("Sorted Order: ",mcArr);
}
}
聲明接口
上一節(jié)使用的是BCL中已有的接口⌒垢簦現(xiàn)在我們來看看如何聲明接口。
關(guān)于聲明接口宛徊,需要知道的重要事項如下:
- 接口聲明不能包含以下成員
- 數(shù)據(jù)成員
- 靜態(tài)成員
- 接口聲明只能包含如下類型的非靜態(tài)成員函數(shù)的聲明
- 方法
- 屬性
- 事件
- 索引器
- 這些函數(shù)成員的聲明不能包含任何實現(xiàn)代碼佛嬉,只能用分號
- 按照慣例,接口名稱以大寫字母I(Interface)開始
- 與類和結(jié)構(gòu)一樣闸天,接口聲明也可以分布
例:兩個方法成員接口的聲明
關(guān)鍵字 接口名稱
↓ ↓
interface IMyInterface1
{
int DoStuff(int nVar1,long lVar2); //分號替代了主體
double DoOtherStuff(string s,long x);
}
接口的訪問性和接口成員的訪問性之間有一些重要區(qū)別
- 接口聲明可以有任何的訪問修飾符public暖呕、protected、internal或private
- 然而苞氮,接口的成員是隱式public的湾揽,不允許有任何訪問修飾符,包括public
接口允許訪問修飾符
↓
public interface IMyInterface2
{
private int Method1(int nVar1); //錯誤
↑
接口成員不允許訪問修飾符
}
實現(xiàn)接口
只有類和結(jié)構(gòu)才能實現(xiàn)接口。
- 在基類列表中包括接口名稱
- 實現(xiàn)每個接口成員
例:MyClass實現(xiàn)IMyInterface1接口
class MyClass:IMyInterface1
{
int DoStuff(int nVar1,long lVar2)
{...} //實現(xiàn)代碼
double DoOtherStuff(string s,long x)
{...} //實現(xiàn)代碼
}
關(guān)于實現(xiàn)接口库物,需要了解以下重要事項:
- 如果類實現(xiàn)了接口霸旗,它必須實現(xiàn)接口的所有成員
- 如果類從基類繼承并實現(xiàn)了接口,基類列表中的基類名稱必須放在所有接口之前戚揭。
基類必須放在最前面 接口名
↓ ↓
class Derived:MyBaseClass,IIfc1,IEnumerable,IComparable
簡單接口示例
interface IIfc1
{
void PrintOut(string s);
}
class MyClass:IIfc1
{
public void PrintOut(string s)
{
Console.WriteLine("Calling through: {0}",s);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
mc.PrintOut("object");
}
}
接口是引用類型
接口不僅是類或結(jié)構(gòu)要實現(xiàn)的成員列表诱告。它是一個引用類型。
我們不能直接通過類對象的成員訪問接口毫目。然而蔬啡,我們可以通過把類對象引用強(qiáng)制轉(zhuǎn)換為接口類型來獲取指向接口的引用。一旦有了接口引用镀虐,我們就可以使用點號來調(diào)用接口方法箱蟆。
例:從類對象引用獲取接口引用
IIfc1 ifc=(IIfc1)mc; //轉(zhuǎn)換為接口,獲取接口引用
ifc.PrintOut("interface"); //使用接口的引用調(diào)用方法</pre>
例:類和接口的引用
interface IIfc1
{
void PrintOut(string s);
}
class MyClass:IIfc1
{
public void PrintOut(string s)
{
Console.WriteLine("Calling through: {0}",s);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
mc.PrintOut("object"); //調(diào)用類對象的實現(xiàn)方法
IIfc1 ifc=(IIfc1)mc;
ifc.PrintOut("interface"); //調(diào)用引用方法
}
}
接口和as運(yùn)算符
上一節(jié)刮便,我們已經(jīng)知道可以使用強(qiáng)制轉(zhuǎn)換運(yùn)算符來獲取對象接口引用空猜,另一個更好的方式是使用as運(yùn)算符。
如果我們嘗試將類對象引用強(qiáng)制轉(zhuǎn)換為類未實現(xiàn)的接口的引用恨旱,強(qiáng)制轉(zhuǎn)換操作會拋出異常辈毯。我們可以通過as運(yùn)算符來避免該問題。
- 如果類實現(xiàn)了接口搜贤,表達(dá)式返回指向接口的引用
- 如果類沒有實現(xiàn)接口谆沃,表達(dá)式返回null
ILiveBirth b=a as ILiveBirth;
if(b!=null)
{
Console.WriteLine("Baby is called: {0}",b.BabyCalled());
}
實現(xiàn)多個接口
- 類或結(jié)構(gòu)可以實現(xiàn)多個接口
- 所有實現(xiàn)的接口必須列在基類列表中并以逗號分隔(如果有基類名稱,則在其后)
interface IDataRetrieve{int GetData();}
interface IDataStore{void SetData(int x);}
class MyData:IDataRetrieve,IDataStore
{
int Mem1;
public int GetData(){return Mem1;}
public void SetData(int x){Mem1=x;}
}
class Program
{
static void Main()
{
var data=new MyData();
data.SetData(5);
Console.WriteLine("Value = {0}",data.GetData());
}
}
實現(xiàn)具有重復(fù)成員的接口
由于接口可以多實現(xiàn)仪芒,有可能多個接口有相同的簽名和返回類型唁影。編譯器如何處理這種情況呢?
例:IIfc1和IIfc2具有相同簽名
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
答案是:如果一個類實現(xiàn)了多接口掂名,并且其中有些接口有相同簽名和返回類型据沈,那么類可以實現(xiàn)單個成員來滿足所有包含重復(fù)成員的接口。
例:MyClass 類實現(xiàn)了IIfc1和IIfc2.PrintOut滿足了兩個接口的需求饺蔑。
class MyClass:IIfc1,IIfc2
{
public void PrintOut(string s)//兩個接口單一實現(xiàn)
{
Console.WriteLine("Calling through: {0}",s);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
mc.PrintOut("object");
}
}
多個接口的引用
如果類實現(xiàn)了多接口锌介,我們可以獲取每個接口的獨立引用。
例:下面類實現(xiàn)了兩個具有當(dāng)PrintOut方法的接口猾警,Main中以3種方式調(diào)用了PrintOut孔祸。
- 通過類對象
- 通過指向IIfc1接口的引用
- 通過指向IIfc2接口的引用
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
class MyClass:IIfc1,IIfc2
{
public void PrintOut(string s)
{
Console.WriteLine("Calling through: {0}",s);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
IIfc1 ifc1=(IIfc1)mc;
IIfc2 ifc2=(IIfc2)mc;
mc.PrintOut("object");
ifc1.PrintOut("interface 1");
ifc2.PrintOut("interface 2");
}
}
派生成員作為實現(xiàn)
實現(xiàn)接口的類可以從它的基類繼承實現(xiàn)的代碼。
例:演示 類從基類代碼繼承了實現(xiàn)
- IIfc1是一個具有PrintOut方法成員的接口
- MyBaseClass包含一個PrintOut方法肿嘲,它和IIfc1匹配
- Derived類有空的聲明主體融击,但它派生自MyBaseClass,并在基類列表中包含了IIfc1
- 即使Derived的聲明主體為空雳窟,基類中的代碼還是能滿足實現(xiàn)接口方法的需求
interface IIfc1
{
void PrintOut(string s);
}
class MyBaseClass
{
public void PrintOut(string s)
{
Console.WriteLine("Calling through: {0}",s);
}
}
class Derived:MyBaseClass,IIfc1
{
}
class Program
{
static void Main()
{
var d=new Derived();
d.PrintOut("object");
}
}
顯式接口成員實現(xiàn)
上面尊浪,我們已經(jīng)看到了單個類可以實現(xiàn)多個接口需要的所有成員匣屡。
但是,如果我們希望為每個接口分離實現(xiàn)該怎么做呢拇涤?這種情況下捣作,我們可以創(chuàng)建顯式接口成員實現(xiàn)。
- 與所有接口實現(xiàn)相似鹅士,位于實現(xiàn)了接口的類或結(jié)構(gòu)中
- 它使用限定接口名稱來聲明券躁,由接口名稱和成員名稱以及它們中間的點分隔符號構(gòu)成
class MyClass:IIfc1,IIfc2
{
void IIfc1.PrintOut(string s)
{...}
void IIfc2.PrintOut(string s)
{...}
}
例:MyClass為兩個解耦的成員聲明了顯式接口成員實現(xiàn)。
interface IIfc1
{
void PrintOut(string s);
}
interface IIfc2
{
void PrintOut(string t);
}
class MyClass:IIfc1,IIfc2
{
void IIfc1.PrintOut(string s)
{
Console.WriteLine("IIfc1: {0}",s);
}
void IIfc2.PrintOut(string s)
{
Console.WriteLine("IIfc2: {0}",s);
}
}
class Program
{
static void Main()
{
var mc=new MyClass();
IIfc1 ifc1=(IIfc1)mc;
ifc1.PrintOut("interface 1");
IIfc2 ifc2=(IIfc2)mc;
ifc2.PrintOut("interface 2");
}
}
如果有顯式接口成員實現(xiàn)掉盅,類級別的實現(xiàn)是允許的也拜,但不是必需的。顯式實現(xiàn)滿足了類或結(jié)構(gòu)必須實現(xiàn)的方法趾痘。因此慢哈,我們可以有如下3種實現(xiàn)場景。
- 類級別實現(xiàn)
- 顯式接口成員實現(xiàn)
- 類級別和顯式接口成員實現(xiàn)
訪問顯式接口成員實現(xiàn)
顯式接口成員實現(xiàn)只可以通過指向接口的引用來訪問永票。即其他的類成員都不可以直接訪問它們卵贱。
例:如下MyClass顯式實現(xiàn)了IIfc1接口。注意侣集,即使是MyClass的另一成員Method1键俱,也不可以直接訪問顯式實現(xiàn)。
- Method1的前兩行編譯錯誤世分,因為方法在嘗試直接訪問實現(xiàn)
- 只有Method1的最后一行代碼才可以編譯编振,因此它強(qiáng)制轉(zhuǎn)換當(dāng)前對象的引用(this)為接口類型的引用,并使用這個指向接口的引用來調(diào)用顯式接口實現(xiàn)
class MyClass:IIfc1
{
void IIfc1.PrintOut(string s)
{
Console.WriteLine("IIfc1");
}
public void Method1()
{
PrintOut("..."); //編譯錯誤
this.PrintOut("..."); //編譯錯誤
((IIfc1)this).PrintOut("...");
↑
轉(zhuǎn)換為接口引用
}
}
這個限制對繼承產(chǎn)生了重要影響臭埋。由于其他類成員不能直接訪問顯式接口成員實現(xiàn)党觅,衍生類的成員也不能直接訪問它們。它們必須總是通過接口的引用來訪問斋泄。
接口可以繼承接口
之前我們已經(jīng)知道接口實現(xiàn)可以從基類繼承,而接口本身也可以從一個或多個接口繼承镐牺。
- 要指定某個接口繼承其他的接口炫掐,應(yīng)在接口聲明中把某接口以逗號分隔的列表形式放在接口名稱的冒號之后
- 與類不同,它在基類列表中只能有一個類名睬涧,接口可以在基接口列表中有任意多個接口
- 列表中的接口本身可以繼承其他接口
- 結(jié)果接口包含它聲明的所有接口和所有基接口的成員
interface IDataIO:IDataRetrieve,IDataStore
{
...
}
例:IDataIO接口繼承了兩個接口
interface IDataRetrieve
{
int GetData();
}
interface IDataStore
{
void SetData(int x);
}
interface IDaTaIO:IDataRetrieve,IDataStore
{
}
class MyData:IDataIO
{
int nPrivateData;
public int GetData()
{
return nPrivateData;
}
public void SetData(int x)
{
nPrivateData=x;
}
}
class Program
{
static void Main()
{
var data=new MyData();
data.SetData(5);
Console.WriteLine("{0}",data.GetData());
}
}
不同類實現(xiàn)一個接口的示例
interface ILiveBirth //聲明接口
{
string BabyCalled();
}
class Animal{} //基類Animal
class Cat:Animal,ILiveBirth //聲明Cat類
{
string ILiveBirth.BabyCalled()
{
return "kitten";
}
}
class Dog:Animal,ILiveBirth //聲明DOg類
{
string ILiveBirth.BabyCalled()
{
return "puppy";
}
}
class Bird:Animal //聲明Bird類
{
}
class Program
{
static void Main()
{
Animal[] animalArray=new Animal[3];
animalArray[0]=new Cat();
animalArray[1]=new Bird();
animalArray[2]=new Dog();
foreach(Animal a in animalArray)
{
ILiveBirth b= a as ILiveBirth;//如果實現(xiàn)ILiveBirth
if(b!=null)
{
Console.WriteLine("Baby is called: {0}",b.BabyCalled());
}
}
}
}
作者:Moonache
出處:http://www.cnblogs.com/moonache/
原文鏈接:https://www.cnblogs.com/moonache/p/6346424.html