|
本文中所有圖示純?yōu)閭€(gè)人理解(參考了Assembly中元數(shù)據(jù)的存儲方式),與真實(shí)情況可能有所出入。 圖中綠色表示公有方法,紅色表示私有方法。
本文將通過以下四個(gè)案例來分析C#中的接口究竟是如何工作的。
1、公有方法實(shí)現(xiàn)接口方法
盡管C#在定義接口時(shí)不用指明接口方法的訪問控制方式,但默認(rèn)接口方法均為public型(這可以從反編譯的IL代碼中看到)。下面是使用Reflector查看的接口IL代碼
.class private interface abstract auto ansi IControl{ .method public hidebysig newslot abstract virtual instance void Paint() cil managed { }}
實(shí)現(xiàn)接口的類需要實(shí)現(xiàn)所有接口方法。通常情況下,接口的實(shí)現(xiàn)方法也為public型。如下案例:
using System ;interface IControl { void Paint();}public class EditBox: IControl { public void Paint() { Console.WriteLine("Pain method is called!"); }}class Test { static void Main() { EditBox editbox = new EditBox(); editbox.Paint(); ((IControl)editbox).Paint(); }}
程序的執(zhí)行結(jié)果為:
Pain method is called!Pain method is called!
接口就好像是關(guān)系型數(shù)據(jù)庫中的一對多表,一個(gè)接口對應(yīng)多個(gè)接口方法,每個(gè)接口方法又對應(yīng)虛擬方法表(VMT)中的某個(gè)公有或私有方法。上面代碼在內(nèi)存中的鏡像可由下圖描述:
從圖中我們可以看到直接對Paint方法的調(diào)用以及通過接口對Paint方法的調(diào)用。可見通過接口對方法進(jìn)行調(diào)用需要多出一道轉(zhuǎn)換工作,因此執(zhí)行效率不如直接調(diào)用。
2、私有方法不能實(shí)現(xiàn)接口方法
如果想將接口方法直接實(shí)現(xiàn)為私有方法是辦不到的。下面的EditBox的代碼中Paint方法沒有特殊說明,默認(rèn)為private,導(dǎo)致代碼無法執(zhí)行:
using System ;interface IControl { void Paint();}public class EditBox: IControl { void Paint() { Console.WriteLine("Pain method is called!"); } public void ShowPaint() { this.Paint(); ((IControl)this).Paint(); }}class Test { static void Main() { EditBox editbox = new EditBox(); editbox.ShowPaint(); }}
程序在編譯時(shí)將顯示如下編譯錯(cuò)誤:““EditBox”不會實(shí)現(xiàn)接口成員“IControl.Paint()”。“EditBox.Paint()”或者是靜態(tài)、非公共的,或者有錯(cuò)誤的返回類型。”
為什么會這樣呢?如圖:
這是由于接口規(guī)范中的方法默認(rèn)的訪問權(quán)限是public,而類中的默認(rèn)訪問權(quán)限是default,也就是說private,因此導(dǎo)致權(quán)限范圍收縮,兩者權(quán)限并不相同,所以必須將類的權(quán)限調(diào)整為public才可以使上面的代碼得以執(zhí)行。
3、實(shí)現(xiàn)專門的接口方法(1)
代碼如下:
using System ;interface IControl { void Paint();}public class EditBox: IControl { void Paint() { Console.WriteLine("Pain method is called!"); } void IControl.Paint() { Console.WriteLine("IControl.Pain method is called!"); } public void ShowPaint() { this.Paint(); ((IControl)this).Paint(); }}class Test { static void Main() { EditBox editbox = new EditBox(); editbox.ShowPaint(); //editbox.Paint(); ((IControl)editbox).Paint(); }}
EditBox類擁有一私有Paint方法,但這并不是接口方法的實(shí)現(xiàn)(上例已經(jīng)分析過)。EditBox類中還包含了一“void IControl.Paint()”方法, 是該方法復(fù)寫了接口的Paint方法,該方法是私有的(通過IL代碼可以看出)。
注意:“void IControl.Paint()”前不能加任何的修飾限定符號,諸如public、private等,這在C#的語法中是不允許的。該方法反編譯得到的IL代碼如下:
.class public auto ansi beforefieldinit EditBox extends object implements IControl{ ....... .method private hidebysig newslot virtual final instance void IControl.Paint() cil managed { .override IControl::Paint }}
程序運(yùn)行時(shí)內(nèi)存中的鏡像可簡化表示為:
程序執(zhí)行結(jié)果如下:
Pain method is called!IControl.Pain method is called!IControl.Pain method is called!
我們之所以可以通過((IControl)editbox).Paint()
方法訪問到代碼是因?yàn)榻涌诜椒≒aint是公有的。但是我們不能通過editbox.Paint()
方法訪問到代碼是因?yàn)镋ditBox的Paint方法是私有的。 在EditBox內(nèi)部,通過ShowPaint方法可以同時(shí)訪問私有的Paint方法與接口IControl.Paint
方法。
4、實(shí)現(xiàn)專門的接口方法(2)
如果EditBox中的Pait方法為公有并且同時(shí)提供了IControl.Paint方法,程序?qū)⑹侨绾芜\(yùn)行的呢?代碼如下:
using System ;interface IControl { void Paint();}public class EditBox: IControl { public void Paint() { Console.WriteLine("Pain method is called!"); } void IControl.Paint() { Console.WriteLine("IControl.Pain method is called!"); }}class Test { static void Main() { EditBox editbox = new EditBox(); editbox.Paint(); ((IControl)editbox).Paint(); }}
程序執(zhí)行結(jié)果如下:
Pain method is called!IControl.Pain method is called!
程序執(zhí)行時(shí)內(nèi)存布局如下:
可見,EditBox中公有的Paint方法并不是接口實(shí)現(xiàn)方法,真正的接口實(shí)現(xiàn)方法是IControl.Paint,這將導(dǎo)致editbox.Paint()
方法與((IControl)editbox).Paint()
的執(zhí)行結(jié)果并不一樣。
5、結(jié)論
接口方法的實(shí)現(xiàn)通常是通過類中的公有方法實(shí)現(xiàn)的;
在一些特殊情況下(代碼隱藏、一個(gè)類實(shí)現(xiàn)的兩個(gè)接口具有相同的接口方法等),需要專門實(shí)現(xiàn)某個(gè)接口的方法。
NET技術(shù):C#中的接口,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。