C#的接口教學(xué)課件_第1頁
C#的接口教學(xué)課件_第2頁
C#的接口教學(xué)課件_第3頁
C#的接口教學(xué)課件_第4頁
已閱讀5頁,還剩28頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

C#的接口ー、什么是接口二、定義接口三、定義接口成員四、訪問接口五、實(shí)現(xiàn)接口六、接口轉(zhuǎn)換七、覆蓋虛接口ハ、事件與接口編程ー、什么是接口第一節(jié)接口慨述接口(interface)用來定義ー種程序的協(xié)定.實(shí)現(xiàn)接口的類或者結(jié)構(gòu)要與接口的定義嚴(yán)格一致。有了這個協(xié)定,就可以拋開編程語言的限制(理論上)。接口可以從多個基接口繼承,而類或結(jié)構(gòu)可以實(shí)現(xiàn)多個接口。接口可以包含方法、屬性、事件和索引器。接口本身不提供它所定義的成員的實(shí)現(xiàn)。接口只指定實(shí)現(xiàn)該接口的類或接口必須提供的成員。接口好比一種模版,這種模版定義了對象必須實(shí)現(xiàn)的方法,貝田的就是讓這些方法可以作為接口實(shí)例被引用。接口不能被實(shí)例化。類可以實(shí)現(xiàn)多個接口并且通過這些實(shí)現(xiàn)的接口被索引。接口變量只能索引實(shí)現(xiàn)該接口的類的實(shí)例。例子:interfaceIMyExample{stringthis[intindex]{get;set;}eventEventHandlerEven;voidFind(intvalue);stringPoint{get;set;})publicdelegatevoidEventHandler(objectsender,Evente);上面例子中的接口包含ー個索引this、ー個事件Even、一個方法Find和一個屬性Point。接口可以支持多重繼承。就像在卜.例中,接口”IComboBox”同時從“ITextBox“和"IListBox”繼承。interfaceIControl{voidPaint();)interfaceITextBox:IControl{voidSetText(stringtext);)interfaceIListBox:IControl{voidSetltems(string[]items);)interfaceIComboBox:ITextBox,IListBox{}類和結(jié)構(gòu)可以多重實(shí)例化接口。就像在下例中,類"EditBox"繼承了類"Control”,同時從"IDataBound"和"IControl"繼承.interfaceIDataBound{voidBind(Binderb);)publicclassEditBox:Control,IControl,IDataBound{publicvoidPaint();publicvoidBind(Binderb){...})在上面的代碼中,"Paint”方法從“IControl"接口而來;"Bind"方法從"IDataBound”接口而來,都以”public"的身份在"EditBox”類中實(shí)現(xiàn)。說明:1、C#中的接口是獨(dú)立于類來定義的。這與C++模型是對立的,在C++中接口實(shí)際上就是抽象基類。2,接口和類都可以繼承多個接口。3,而類可以繼承一個基類,接口根本不能繼承類。這種模型避免了C++的多繼承問題,C++中不同基類中的實(shí)現(xiàn)可能出現(xiàn)沖突。因此也不再需要諸如虛擬繼承和顯式作用域這類復(fù)雜機(jī)制。C#的簡化接u模型有助于加快應(yīng)用程序的開發(fā)。4,ー個接口定義ー個只有抽象成員的引用類型。C#中一個接口實(shí)際所做的,僅僅只存在著方法標(biāo)志,但根本就沒有執(zhí)行代碼。這就暗示了不能實(shí)例化?個接口,只能實(shí)例化ー個派生自該接口的對象。5,接ロ可以定義方法、屬性和索引。所以,對比個類,接口的特殊性是:當(dāng)定義?個類時,可以派生自多重接口,而你只能可以從僅有的ー個類派生。接口與組件接口描述了組件對外提供的服務(wù)。在組件和組件之間、組件和客戶之間都通過接口進(jìn)行交互。因此組件?旦發(fā)布,它只能通過預(yù)先定義的接口來提供合理的、一致的服務(wù)。這種接口定義之間的穩(wěn)定性使客戶應(yīng)用開發(fā)者能夠構(gòu)造出堅(jiān)固的應(yīng)用。?個組件可以實(shí)現(xiàn)多個組件接口,而?個特定的組件接口也可以被多個組件來實(shí)現(xiàn)。組件接口必須是能夠自我描述的。這意味著組件接口應(yīng)該不依賴于具體的實(shí)現(xiàn),將實(shí)現(xiàn)和接口分離徹底消除了接口的使用者和接口的實(shí)現(xiàn)者之間的耦合關(guān)系,增強(qiáng)了信息的封裝程度。同時這也要求組件接口必須使用ー種與組件實(shí)現(xiàn)無關(guān)的語言。□前組件接口的描述標(biāo)準(zhǔn)是!DL語言。由于接口是組件之間的協(xié)議,因此組件的接口一旦被發(fā)布,組件生產(chǎn)者就應(yīng)該盡可能地保持接口不變,任何對接口語法或語義上的改變,都有可能造成現(xiàn)有組件與客戶之間的聯(lián)系遭到破壞。每個組件都是自主的,有其獨(dú)特的功能,只能通過接口與外界通信。當(dāng)ー個組件需耍提供新的服務(wù)時,可以通過增加新的接口來實(shí)現(xiàn)。不會影響原接口已存在的客戶。而新的客戶可以重新選擇新的接口來獲得服務(wù)。組件化程序設(shè)計組件化程序逡辻方法繼承并發(fā)展了面向?qū)ο蟮某绦蛟O(shè)計方法。它把對象技術(shù)應(yīng)用于系統(tǒng)設(shè)計,對面向?qū)ο蟮某绦蛟O(shè)計的實(shí)現(xiàn)過程作了進(jìn)ー步的抽象。我們可以把組件化程序設(shè)計方法用作構(gòu)造系統(tǒng)的體系結(jié)構(gòu)層次的方法,并且可以使用面向?qū)ο蟮姆椒ê芊奖愕貙?shí)現(xiàn)組件。組件化程序設(shè)計強(qiáng)調(diào)真正的軟性可重用性和高度的互操作性。它側(cè)重于組件的產(chǎn)生和裝配,這兩方面一起構(gòu)成了組件化程序設(shè)計的核心。組件的產(chǎn)生過程不僅僅是應(yīng)用系統(tǒng)的需求,組件市場本身也推動了組件的發(fā)展,促進(jìn)了軟件廠商的交流與合作。組件的裝配使得軟件產(chǎn)品可以采用類似于搭積木的方法快速地建立起來,不僅可以縮短軟件產(chǎn)品的開發(fā)周期,同時也提高了系統(tǒng)的穩(wěn)定性和可靠性。組件程序設(shè)計的方法有以下幾個方面的特點(diǎn):1,編程語言和開發(fā)環(huán)境的獨(dú)立性:2、組件位置的透明性;3、組件的進(jìn)程透明性;4、可擴(kuò)充性;5、可重用性:6、具有強(qiáng)有力的基礎(chǔ)設(shè)施;7、系統(tǒng)ー級的公共服務(wù);C#語言由丁?其許多優(yōu)點(diǎn),十分適用于組件編程。但這并不是說C#是ー門組件編程語言,也不是說〇#提供了組件編程的工具。我們已經(jīng)多次指出,組件應(yīng)該具有與編程語言無關(guān)的特性。請讀者記住這一點(diǎn);組件模型是ー種規(guī)范,不管采用何種程序語言設(shè)計組件,都必須遵守這ー規(guī)范。比如組裝計算機(jī)的例子,只耍各個廠商為我們提供的配件規(guī)格、接口符合統(tǒng)ー的標(biāo)準(zhǔn),這些配件組合起來就能協(xié)同工作,組件編程也是ー樣。我們只是說,利用C#語言進(jìn)行組件編程將會給我們帶來更大的方便。知道了什么是接口,接下來就是怎樣定義接口,請看下ー節(jié)一定義接口。二、定義接口從技術(shù)上講,接口是?組包含了函數(shù)型方法的數(shù)據(jù)結(jié)構(gòu)。通過這組數(shù)據(jù)結(jié)構(gòu),客戶代碼可以調(diào)用組件對象的功能。定義接口的?般形式為;[attributes][modifiers]interfaceidentifier[:base-list]{interface-body)[;]說明;1、attributes(可選):附加的定義性信息。2、modifiers(可選);允許使用的修飾符有new和四個訪問修飾符。分別是:new、public、protected、internal、private?在ー個接口定義中同一修飾符不允許出現(xiàn)多次,new修飾符只能出現(xiàn)在嵌套接口中,表示覆蓋了繼承而來的同名成員。Thepublic,protected,internal,andprivate修飾符定義了對接口的訪問權(quán)限。3、指示3和事件。4,identifier:接口名稱。5、base-list(可選):包含一個或多個顯式基接口的列表,接口間由逗號分隔。interface-body:對接口成員的定義。7、接ロ可以是命名空間或類的成員,并且可以包含下列成員的簽名:方法、屬性、索引器。8、ー個接口可從ー個或多個基接口繼承。接口這個概念在C#和出里中非常相似。接口的關(guān)鍵詞是interface,一個接口可以擴(kuò)展一個或者多個其他接口。按照慣例,接口的名字以大寫字母叩開頭。下面的代碼是C#接口的ー個例子,它與Java中的接口完全ー樣:interfaceIShape{voidDraw();}如果你從兩個或者兩個以上的接口派生,父接口的名字列表用逗號分隔,如下面的代碼所示:interfaceINewInterface:IParentl,IParent2{}然而,與Java不同,C#中的接口不能包含域(Field)〇另外還要注意,在C#中,接口內(nèi)的所有方法默認(rèn)都是公用方法。在Java中,方法定義可以帶有public修飾符(即使這并非必要),但在C#中,顯式為接口的方法指定public修飾符是非法的。例如,ド面的〇#接口將產(chǎn)生一個編譯錯誤。interfaceIShape{publicvoidDraw();}下面的例子定義了一個名為Control的接口,接口中包含ー一個成員方法Paint:interfaceIControl{voidPaint();)在下例中,接口interface從兩個基接口Easel和舊ase2繼承:interface(Interface:IBasel,IBase2{voidMethodi();voidMethod2();)接口可由類實(shí)現(xiàn)。實(shí)現(xiàn)的接口的標(biāo)識符出現(xiàn)在類的基列表中。例如:classClassi:Ifacel,Iface2{//class成員。)類的基列表同時包含基類和接口時,列表中首先出現(xiàn)的是基類。例如:classClassA:BaseClass,Ifacel,Iface2{//class成員。)以下的代碼段定義接口IFace,它只有一個方法:interfaceIFace{voidShowMyFace();)不能從這個定義實(shí)例化一個對象,但可以從它派生一個類。因此,該類必須實(shí)現(xiàn)ShowMyFace抽象方法:classCFace:IFace{publicvoidShowMyFace(){Console.WriteLine(Mimplementation");基接口一個接口可以從零或多個接u繼承,那些被稱為這個接口的顯式基接口.當(dāng)ー個接口有比零多的顯式基接口時,那么在接口的定義中的形式為,接口標(biāo)識符后面跟著由?個冒號”:“和一個用逗號”,“分開的基接口標(biāo)識符列表。接口基::接口類型列表說明:1、ー個接口的顯式基接口必須至少同接口本身?樣可訪問。例如,在?個公共接口的基接口中指定一個私有或內(nèi)部的接口是錯誤的。2、ー個接口直接或間接地從它自己繼承是錯誤的。3、接ロ的基接口都是顯式基接口,并且是它們的基接口。換句話說,基接口的集合完全由顯式基接口和它們的顯式基接口等等組成。在下面的例子中interfaceIControl{voidPaint();}interfaceITextBox:IControl{voidSetText(stringtext);}interfaceIListBox:IControl{voidSetltems(string[]items);)interfaceIComboBox:ITextBox,IListBox{}IComboBox的基接口是IControl,ITextBox,和IlistBoxo4、ー個接口繼承它的基接口的所有成員。換句話說,上面的接口IComboBox就像Paintー樣繼承成員SetText和Setitems〇?個實(shí)現(xiàn)了接口的類或結(jié)構(gòu)也隱含地實(shí)現(xiàn)了所有接口的基接口。接口主體?個接口的接口主體定義接口的成員。interface-body:{interface-member-declarationsopt}定義接口主要是定義接口成員,請看下ー節(jié)一定義接口成員。三、定義接口成員接口可以包含一個和多個成員,這些成員可以是方法、屬性、索引指示器和事件,但不能是常鼠、域、操作符、構(gòu)造函數(shù)或析構(gòu)函數(shù),而且不能包含任何靜態(tài)成員。接口定義創(chuàng)建新的定義空間,并且接口定義直接包含的接口成員定義將新成員引入該定義空間。說明:1、接口的成員是從基接口繼承的成員和由接口本身定義的成員。2、接口定義可以定義零個或多個成員。接口的成員必須是方法、屬性、事件或索引器。接口不能包含常數(shù)、字段、運(yùn)算符、實(shí)例構(gòu)造函數(shù)、析構(gòu)函數(shù)或類型,也不能包含任何種類的靜態(tài)成員。3、定義一個接口,該接口對于每種可能種類的成員都包含ー個:方法、屬性、事件和索引器。4、接口成員默認(rèn)訪問方式是public。接口成員定義不能包含任何修飾符,比如成員定義前不能加abstract,public,protected,internal,private,virtual,override或static修飾符。5、接口的成員之間不能相互同名。繼承而來的成員不用再定義,但接口可以定義與繼承而來的成員同名的成員,這時我們說接口成員覆蓋了繼承而來的成員,這不會導(dǎo)致錯誤,但編譯器會給出ー個警告。關(guān)閉警告提示的方式是在成員定義前加上一個new關(guān)鍵字。但如果沒有覆蓋父接口中的成員,使用new關(guān)鍵字會導(dǎo)致編譯器發(fā)出警告。6、方法的名稱必須與同一接口中定義的所有屬性和事件的名稱不同。此外,方法的簽名必須與同一接口中定義的所有其他方法的簽名不同。7、屬性或事件的名稱必須與同一接口中定義的所有其他成員的名稱不同。8、ー個索引器的簽名必須區(qū)別了在同一接口中定義的其他所有索引器的簽名。9、接口方法聲明中的屬性(attributes),返回類型(return-type),標(biāo)識符(identifier),和形式參數(shù)列表(formal-parameter-lis)與一個類的方法聲明中的那些有相同的意義。ー個接口方法聲明不允許指定一個方法主體,而聲明通常用ー個分號結(jié)束。10、接口屬性聲明的訪問符與類屬性聲明的訪問符相對應(yīng),除了訪問符主體通常必須用分號。因此,無論屬性是讀寫、只讀或只寫,訪問符都完全確定。11、接ロ索引聲明中的屬性(attributes),類型(type),和形式參數(shù)列表(formal-parameter-list)與類的索引聲明的那些有相同的意義。下面例子中接口IMyTest包含了索引指示器、事件E、方法F、屬性P這些成員:interfaceIMyTest{stringthis[intindex]{get;set;}eventEventHandlerE;voidF(intvalue);stringP{get;set;}}publicdelegatevoidEventHandler(objectsender,EventArgse);卜面例子中接口IStringList包含每個可能類型成員的接口:一個方法,ー個屬性,ー個事件和一個索弓I。publicdelegatevoidStringListEvent(IStringListsender);publicinterfaceIStringList(voidAdd(strings);intCount{get;}eventStringListEventChanged;stringthis[intindex]{get;set;})接口成員的全權(quán)名使用接口成員也可釆用全權(quán)名(fullyqualifiedname).接口的全權(quán)名稱是這樣構(gòu)成的。接口名加小圓點(diǎn)”."再跟成員名比如對于下面兩個接口:interfaceIControl{voidPaint();)interfaceITextBox:IControl{voidGetText(stringtext);)其中Paint的全權(quán)名是IControLPaint,GetText的全權(quán)名是ITextBox.GetText。當(dāng)然,全權(quán)名中的成員名稱必須是在接口中已經(jīng)定義過的,比如使用ITextBox.Paint.就是不合理的。如果接口是名字空間的成員,全權(quán)名還必須包含名字空間的名稱。namespaceSystem(publicinterfaceIDataTable{objectClone();)}那么Clone方法的全權(quán)名是System.IDataTable.Clone?定義好了接口,接下來就是怎樣訪問接口,請看下ー節(jié)ー訪問接口四、訪問接口對接口成員的訪問對接口方法的調(diào)用和采用索引指示器訪問的規(guī)則與類中的情況也是相同的。如果底層成員的命名與繼承而來的髙層成員一致,那么底層成員將覆蓋同名的高層成員.但由于接口支持多繼承,在多繼承中,如果兩個父接口含有同名的成員,這就產(chǎn)生了二義性(這也正是C#中取消了類的多繼承機(jī)制的原因之一),這時需耍進(jìn)行顯式的定義:usingSystem;interface(Sequence{intCount{get;set;})interfaceIRing{voidCount(inti);)interfaceIRingSequence:ISequence,IRing{}classCTest{voidTest(IRingSequencers){//rs.Count(1);錯誤,Count有二義性//rs.Count=1;錯誤,Count有二義性((ISequence)rs).Count=1;//正確((IRing)rs).Count(l);//正確調(diào)用IRing.Count))上面的例子中,前兩條語句rs.Count(1)和rs.Count=1會產(chǎn)生二義性,從而導(dǎo)致編譯時錯誤,因此必須顯式地給rs指派父接口類型,這種指派在運(yùn)行時不會帶來額外的開銷。再看下面的例子:usingSystem;interfaceIInteger{voidAdd(inti);}interfaceIDouble{voidAdd(doubled);}interfaceINumbe匚llnteger,IDouble{}classCMyTest{voidTest(INumberNum){//Num.Add(1);錯誤Num.Add(I.O);/Z正確((llnteger)n).Add(l);//正確((IDouble)n).Add(l);//正確})調(diào)用Num.Add(1)會導(dǎo)致二義性,因?yàn)楹蜻x的重載方法的參數(shù)類型均適用。但是,調(diào)用Num.Add(LO)是允許的,因?yàn)?.0是浮點(diǎn)數(shù)參數(shù)類型與方法Ilnteger.Add〇的參數(shù)類型不一致,這時只有IDouble.Addオ是適用的。不過只要加入了顯式的指派,就決不會產(chǎn)生二義性。接口的多重繼承的問題也會帶來成員訪問上的問題。例如:interfaceIBase{voidFWay(inti);)interfaceILeft:IBase{newvoidFWay(inti);)interfaceIRight:IBase{voidG();}interfaceDerived:ILeft,IRight{}classCTest{voidTest(IDerivedd){d.FWay(1);/Z調(diào)用ILeft.FWay((IBase)d).FWay(1);/Z調(diào)用IBase.FWay<(ILeft)d).FWay(1);/Z調(diào)用ILeft.FWay((IRight)d).FWay(1);/Z調(diào)用IBase.FWay))上例中,方法舊ase.FWay在派生的接口ILeft屮被lleft的成員方法FWay覆蓋了。所以對d.FWay(1)的調(diào)用實(shí)際上調(diào)用了。雖然從舊ase->IRight->IDerived這條繼承路徑上來看,ILeft.FWay方法是沒有被覆蓋的。我們只要記住這一點(diǎn):一旦成員被覆蓋以后,所有對其的訪問都被覆蓋以后的成員”攔截〃了。類對接口的實(shí)現(xiàn)前面我們已經(jīng)說過,接口定義不包括方法的實(shí)現(xiàn)部分。接口可以通過類或結(jié)構(gòu)來實(shí)現(xiàn)。我們主:要講述通過類來實(shí)現(xiàn)接口。用類來實(shí)現(xiàn)接口時,接口的名稱必須包含在類定義中的基類列表中。下面的例子給出了由類來實(shí)現(xiàn)接口的例子。其中ISequence為ー個隊(duì)列接口,提供了向隊(duì)列尾部添加對象的成員方法Add(),IRing為?個循環(huán)表接口,提供了向環(huán)中插入對象的方法lnsert(objectobj),方法返回插入的位置。類RingSquence實(shí)現(xiàn)了接口ISequence和接口IRing〇usingSystem;interfaceISequence{objectAdd();)interfaceISequence{objectAdd();)interfaceIRing{intlnsert(objectobj);}classRingSequence:ISequence,IRing(publicobjectAdd(){...}publicintlnsert(objectobj)}如果類實(shí)現(xiàn)了某個接口,類也隱式地繼承了該接口的所有父接口,不管這些父接口有沒有在類定義的基類表中列出??聪旅娴睦?usingSystem;interfaceIControl{voidPaint();)interfaceITextBox:IControl{voidSetText(stringtext);}interfaceIListBox:IControl{voidSetltems(string[]items);)interfaceIComboBox:ITextBox,IListBox{}這里,接口IcomboBox繼承了ItextBox和!listBoxo類TextBox不僅實(shí)現(xiàn)了接口ITextBox,還實(shí)現(xiàn)了接口ITextBox的父接口IControl〇前面我們已經(jīng)看到,ー個類可以實(shí)現(xiàn)多個接口。再看下面的例子:interfaceIDataBound{voidBind(Binderb);)publicclassEditBox:Control,IControl,IDataBound{publicvoidPaint();publicvoidBind(Binderb){...})類EditBox從類Contro!中派牛.并且實(shí)現(xiàn)了Icontrol和IdataBound。在前面的例子中接口Icontro!中的Paint方法和IdataBound接口中的Bind方法都用類Ed讓Box中的公共成員實(shí)現(xiàn)。C#提供一種實(shí)現(xiàn)這些方法的可選擇的途徑,這樣可以使執(zhí)行這些的類避免把這些成員設(shè)定為公共的。接口成員可以用有效的名稱來實(shí)現(xiàn)。例如,類EditBox可以改作方法Icontrol.PaintネリIdataBound.Bind來來實(shí)現(xiàn)。publicclassEditBox:IControl,IDataBound{voidIControl.Paint(){...}voidIDataBound.Bind(Binderb){...})因?yàn)橥ㄟ^外部指派接口成員實(shí)現(xiàn)了每個成員,所以用這種方法實(shí)現(xiàn)的成員稱為外部接口成員。外部接口成員可以只是通過接口來調(diào)用。例如,Paint方法中EditBox的實(shí)現(xiàn)可以只是通過創(chuàng)建にontrol接口來調(diào)用。classTest{staticvoidMain(){EditBoxeditbox=newEditBox();editbox.Paint();〃錯誤:EditBox沒有Paint事件IControlcontrol=editbox;control.Paint();/Z調(diào)用EditBox的Paint事件)上例中,類EditBox從Control類繼承并同時實(shí)現(xiàn)了IControlandIDataBound接口。EditBox中的Paint方法來自IContro!接口,Bind方法來自IDataBound接口,二者在EditBox類中都作為公有成員實(shí)現(xiàn)。當(dāng)然,在C#中我們也可以選擇不作為公有成員實(shí)現(xiàn)接口。如果每個成員都明顯地指出了被實(shí)現(xiàn)的接口,通過這種途徑被實(shí)現(xiàn)的接口我們稱之為顯式接口成員(explicitinterfacemember)〇用這種方式我們改寫上面的例子:publicclassEditBox:IControl,IDataBound{voidIControl.Paint(){...}voidIDataBound.Bind(Binderb){...})顯式接口成員只能通過接口調(diào)用。例如:classCTest{staticvoidMain(){EditBoxeditbox=newEditBox();editbox.Paint();〃錯誤:不同的方法IControlcontrol=editbox;control.Paint();〃調(diào)用EditBox的Paint方法))上述代碼中對editbox.Paint()的調(diào)用是錯誤的,因?yàn)閑ditbox本身并沒有提供這一方法。control.Paint()是正確的調(diào)用方式。注釋:接口本身不提供所定義的成員的實(shí)現(xiàn),它僅僅說明這些成員,這些成員必須依靠實(shí)現(xiàn)接口的類或其它接口的支持。知道了怎樣訪問接口,我們還要知道怎樣實(shí)現(xiàn)接口,要實(shí)現(xiàn)C#的接口,請看下ー節(jié)?實(shí)現(xiàn)接口五、實(shí)現(xiàn)接口1、顯式實(shí)現(xiàn)接口成員為了實(shí)現(xiàn)接口,類可以定義顯式接口成員執(zhí)行體(Explicitinterfacememberimplementations)。顯式接口成員執(zhí)行體可以是ー個方法、ー個屬性、ー個事件或者是ー個索引指示器的定義,定義與該成員對應(yīng)的全權(quán)名應(yīng)保持一致。usingSystem;interfaceICIoneable{objectClone();)interfaceIComparable{intCompareTo(objectother);)classListEntry:ICIoneable,IComparable{objectICIoneable.Clone(){...}intIComparable.CompareTo(objectother){...}上面的代碼中ICIoneable.Clone和!Comparable.CompareTo就是顯式接口成員執(zhí)行體。說明:1、不能在方法調(diào)用、屬性訪問以及索引指示器訪問屮通過全權(quán)名訪問顯式接U成員執(zhí)行體。事實(shí)上,顯式接U成員執(zhí)行體只能通過接口的實(shí)例,僅僅引用接口的成員名稱來訪問。2、顯式接口成員執(zhí)行體不能使用任何訪問限制符,也不能加上abstract,virtual,override或static修飾符。3,顯式接口成員執(zhí)行體和其他成員有著不同的訪問方式。因?yàn)椴荒茉诜椒ㄕ{(diào)用、屬性訪問以及索引指示器訪問中通過全權(quán)名訪問,顯式接口成員執(zhí)行體在某種意義上是私有的。但它們又可以通過接口的實(shí)例訪問,也具有一定的公有性質(zhì)。4,只有類在定義時,把接口名寫在了基類列表中,而且類屮定義的全權(quán)名、類型和返回類型都與顯式接口成員執(zhí)行體完全?致時,顯式接口成員執(zhí)行體オ是有效的,例如:classShape:ICIoneable{objectICIoneable.Clone(){...}intIComparable.CompareTo(objectother){...})使用顯式接口成員執(zhí)行體通常有兩個目的:1、因?yàn)轱@式接口成員執(zhí)行體不能通過類的實(shí)例進(jìn)行訪問,這就可以從公有接口中把接口的實(shí)現(xiàn)部分單獨(dú)分離開。如果ー個類只在內(nèi)部使用該接口,而類的使用者不會宜接使用到該接口,這種顯式接口成員執(zhí)行體就可以起到作用。2、顯式接口成員執(zhí)行體避免了接口成員之間因?yàn)橥l(fā)生混淆。如果ー個類希望對名稱和返回類型相同的接口成員采用不同的實(shí)現(xiàn)方式,這就必須要使用到顯式接口成員執(zhí)行體。如果沒有顯式接口成員執(zhí)行體,那么對于名稱和返回類型不同的接口成員,類也無法進(jìn)行實(shí)現(xiàn)。下面的定義是無效的,因?yàn)镾hape定義時基類列表中沒有出現(xiàn)接口IComparable。classShape:ICIoneable(objectICIoneable.Clone(){...})classEllipse:Shape(objectICIoneable.Clone(){...})在日lipse中定義ICIoneable.Clone是錯誤的,因?yàn)槿誰ipse即使隱式地實(shí)現(xiàn)了接口ICIoneable,ICIoneable仍然沒有顯式地出現(xiàn)在日lipse定義的基類列表中。接口成員的全權(quán)名必須對應(yīng)在接口中定義的成員。如ド面的例子中,Paint的顯式接口成員執(zhí)行體必須寫成IControl.PaintousingSystem;interfaceIControl(voidPaint();)interfaceITextBox:IControl(voidSetText(stringtext);)classTextBox:ITextBox(voidIControl.Paint(){...}voidITextBox.SetText(stringtext){...})實(shí)現(xiàn)接口的類可以顯式實(shí)現(xiàn)該接口的成員。當(dāng)顯式實(shí)現(xiàn)某成員時,不能通過類實(shí)例訪問該成員,而只能通過該接口的實(shí)例訪問該成員。顯式接口實(shí)現(xiàn)還允許壓楚員繼承共享相同成員名的兩個接口,并為每個接口成員提供ー個單獨(dú)的實(shí)現(xiàn)。下面例子屮同時以公制單位和英制單位顯示框的尺寸。Box類繼承l(wèi)EnglishDimensions和IMetricDimensions兩個接ロ,它們表示不同的度量衡系統(tǒng)。兩個接口有相同的成員名Length和Width。程序清單1DemonInterface.csinterfacelEnglishDimensions{floatLength();floatWidth();}interfaceIMetricDimensions{floatLength();floatWidth();)classBox:lEnglishDimensions,IMetricDimensions{floatlengthinches;floatwidthinches;publicBox(floatlength,floatwidth){lengthinches=length;widthinches=width;)floatlEnglishDimensions.Length(){returnlengthinches;)floatIEnglishDimensions.Width(){returnwidthinches;)floatIMetricDimensions.Length(){returnlengthinches*2.54f;)floatIMetricDimensions.Width(){returnwidthinches*2.54f;)publicstaticvoidMain(){〃定義?個實(shí)類對象"myBox"::BoxmyBox=newBox(30.0f,20.0f);//定義一個接口”eDimensions”::lEnglishDimensionseDimensions=(lEnglishDimensions)myBox;IMetricDimensionsmDimensions=(IMetricDimensions)myBox;/Z輸出:System.Console.WriteLine(HLength(in):{0}M,eDimensions.Length());System.Console.WriteLine(HWidth(in):{〇}",eDimensions.Width());System.Console.WriteLine(HLength(cm):{0}",mDimensions.Length());System.Console.WriteLine("Width(cm):{〇}",mDimensions.Width());))輸出:Length(in):30,Width(in):20,Length(cm):76.2,Width(cm):50.8代碼討論:如果希望默認(rèn)度量采用英制單位,請正常實(shí)現(xiàn)Length和Width這兩個方法,并從IMetricDimensions接口顯式實(shí)現(xiàn)Length和Width方法:publicfloatLength(){returnlengthinches;)publicfloatWidth(){returnwidthinches;)floatIMetricDimensions.Length(){returnlengthinches*2.54f;)floatIMetricDimensions.Width(){returnwidthinches*2.54f;)這種情況下,可以從類實(shí)例訪問英制單位,而從接口實(shí)例訪問公制單位:System.Console.WriteLine("Length(in):{0}",myBox.Length());System.Console.WriteLine("Width(in):{0}",myBox.Width());System.Console.WriteLine("Length(cm):{0}",mDimensions.Length());System.Console.WriteLine("Width(cm):{0}",mDimensions.Width());2、繼承接口實(shí)現(xiàn)接口具有不變性,但這并不意味著接口不再發(fā)展。類似于類的繼承性,接ロ也可以繼承和發(fā)展。注意:接口繼承和類繼承不同,首先,類繼承不僅是說明繼承,而且也是實(shí)現(xiàn)繼承:而接口繼承只是說明繼承。也就是說,派生類可以繼承基類的方法實(shí)現(xiàn),而派生的接口只繼承了父接口的成員方法說明,而沒有繼承父接口的實(shí)現(xiàn),其次,C#中類繼承只允許單繼承,但是接口繼承允許多繼承,ー個子接口可以有多個父接口。接口可以從零或多個接口中繼承。從多個接口中繼承時,用":"后跟被繼承的接口名字,多個接口名之間用”,”分割。被繼承的接口應(yīng)該是可以訪問得到的,比如從private類型或internal類型的接口中繼承就是不允許的。接口不允許直接或間接地從自身繼承。和類的繼承相似,接口的繼承也形成接口之間的層次結(jié)構(gòu)。請看下面的例子:usingSystem;interfaceIControl{voidPaint();)interfaceITextBox:IControl{voidSetText(stringtext);}interfaceIListBox:IControl{voidSetltems(string[]items);)interfaceIComboBox:ITextBox,IListBox{}對?個接口的繼承也就繼承了接口的所有成員,上面的例子中接口ITextBox和IListBox都從接口IControl中繼承,也就繼承了接口IControl的Paint方法。接口IComboBox從接口ITextBox和IListBox屮繼承,因此它應(yīng)該繼承了接口ITextBox的SetText方法和IListBox的Setitems方法,還有IContro!的Paint方法。ー個類繼承了所有被它的基本類提供的接口實(shí)現(xiàn)皿。不通過顯式的實(shí)現(xiàn)ー個接口,ー個派生類不能用任何方法改變它從它的基本類繼承的接口映射。例如,在聲明屮interfaceIControl{voidPaint();)classControl:IControl{publicvoidPaint(){...})classTextBox:Control{newpublicvoidPaint(){...})TextBox中的方法Paint隱藏了Control中的方法Paint,但是沒有改變從Control.Paint到IControl.Paint的映射,而通過類實(shí)例和接口實(shí)例調(diào)用Paint將會有下面的影響Controlc=newControl();TextBoxt=newTextBox();IControlic=c;IControlit=t;c.Paint();/Z影響Control.Paint();t.Paint();/Z影響TextBox.Paint();ic.Paint();/Z影響Control.Paint();it.Paint();/Z影響Control.Paint();但是,當(dāng)?個接口方法被映射到?個類中的虛擬方法,派生類就不可能覆蓋這個虛擬方法并且改變接口的實(shí)現(xiàn)函數(shù)。例如,把上面的聲明重新寫為interfaceIControl{voidPaint();}classControl:IControl{publicvirtualvoidPaint(){...})classTextBox:Control{publicoverridevoidPaint(){...})就會看到下面的結(jié)果:Controlc=newControl();TextBoxt=newTextBox();IControlic=c;IControlit=t;c.Paint();/Z影響Control.Paint();t.Paint();/Z影響TextBox.Paint();ic.Paint();/Z影響Control.Paint();it.Paint();/Z影響TextBox.Paint();由于顯式接口成員實(shí)現(xiàn)程序不能被聲明為虛擬的,就不可能覆蓋一個顯式接口成員實(shí)現(xiàn)程序。一個顯式接口成員實(shí)現(xiàn)程序調(diào)用另外?個方法是有效的,而另外的那個方法可以被聲明為虛擬的以便讓派生類可以覆蓋它。例如:interfaceIControl{voidPaint();)classControl:IControl{voidIControl.Paint(){PaintControl();}protectedvirtualvoidPaintControl(){...})classTextBox:Control{protectedoverridevoidPaintControl(){...}這里,從Control繼承的類可以通過覆蓋方法PaintControl來對IControl.Paint的實(shí)現(xiàn)程序進(jìn)行特殊化。3、重新實(shí)現(xiàn)接口我們已經(jīng)介紹過,派生類可以對基類中已經(jīng)定義的成員方法進(jìn)行重載。類似的概念引入到類對接口的實(shí)現(xiàn)中來,叫做接口的重實(shí)現(xiàn)(re-implementation)〇繼承了接口實(shí)現(xiàn)的類可以對接口進(jìn)行重實(shí)現(xiàn)。這個接口要求是在類定義的基類列表中出現(xiàn)過的。對接口的重實(shí)現(xiàn)也必須嚴(yán)格地遵守首次實(shí)現(xiàn)接口的規(guī)則,派生的接口映射不會對為接口的重實(shí)現(xiàn)所建立的接口映射產(chǎn)生任何影響。下面的代碼給出了接口重實(shí)現(xiàn)的例子:interfaceIControl{voidPaint();classControl:IControlvoidIControl.Paint(){...}classMyControl:Control,IControlpublicvoidPaint(){})實(shí)際上就是:Control把IControl.Paint映射到了ControLIControl.Paint上,但這并不影響在MyControl中的重實(shí)現(xiàn)。在MyContro!中的重實(shí)現(xiàn)中,IControl.Paint被映射到MyControl.Paint之上。在接口的重實(shí)現(xiàn)時,繼承而來的公有成員定義和繼承而來的顯式接口成員的定義參與到接口映射的過程。usingSystem;interfaceIMethods{voidF();voidG();voidH();void1();}classBase:IMethods{voidIMethods.F(){}voidIMethods.G(){}publicvoidH(){}publicvoid1(){})classDerived:Base,IMethods{publicvoidF(){}voidIMethods.H(){})這里,接口IMethods在Derived中的實(shí)現(xiàn)把接口方法映射至リ了Derived.F,Base.IMethods.G,Derived.IMethods.H,還有Base.l。前面我們說過,類在實(shí)現(xiàn)?個接口時,同時隱式地實(shí)現(xiàn)了該接口的所有父接口。同樣,類在重實(shí)現(xiàn)?個接口時同時,隱式地重實(shí)現(xiàn)了該接口的所有父接口。usingSystem;interfaceIBase{voidF();interfaceIDerived:IBase{voidG();)classC:Derived{voidIBase.F(){〃對F進(jìn)行實(shí)現(xiàn)的代円…)voidIDerived.G(){〃對G進(jìn)行實(shí)現(xiàn)的代碼…}}classD:C,IDerived{publicvoidF(){〃對F進(jìn)行實(shí)現(xiàn)的代碼…}publicvoidG(){〃對G進(jìn)行實(shí)現(xiàn)的代碼…}}這里,對IDerived的車實(shí)現(xiàn)也同樣實(shí)現(xiàn)了對舊ase的重實(shí)現(xiàn),把舊ase.F映射到了D.F。4、映射接口類必須為在基類表中列出的所有接口的成員提供具體的實(shí)現(xiàn)。在類中定位接口成員的實(shí)現(xiàn)稱之為接口映射(interfacemapping)。映射,數(shù)學(xué)上表示ーー對應(yīng)的函數(shù)關(guān)系。接口映射的含義也是ー樣,接口通過類來實(shí)現(xiàn),那么対于在接口中定義的每一個成員,都應(yīng)該對應(yīng)著類的?個成員來為它提供具體的實(shí)現(xiàn)。類的成員及其所映射的接口成員之間必須滿足下列條件:1、如果A和B都是成員方法,那么A和B的名稱、類型、形參表(包括參數(shù)個數(shù)和每ー個參數(shù)的類型)都應(yīng)該是ー致的。2、如果A和B都是屬性,那么A和B的名稱、類型應(yīng)當(dāng)一致,而且A和B的訪問器也是類似的。但如果A不是顯式接口成員執(zhí)行體,A允許增加自己的訪問器。3、如果A和B都是時間那么A和B的名稱、類型應(yīng)當(dāng)一致。4、如果A和B都是索引指示器,那么A和B的類型、形參表(包括參數(shù)個數(shù)和每ー個參數(shù)的類型)應(yīng)當(dāng)一致。而且A和B的訪問器也是類似的。但如果A不是顯式接口成員執(zhí)行體,A允許增加自己的訪問器。那么,對于ー個接口成員,怎樣確定由哪ー個類的成員來實(shí)現(xiàn)呢?即ー個接口成員映射的是哪ー個類的成員?在這里,我們敘述ード接口映射的過程。假設(shè)類C實(shí)現(xiàn)了一個接口interface,Member是接口リnterface中的一個成員,在定位由誰來實(shí)現(xiàn)接口成員Member,即Member的映射過程是這樣的:1、如果C中存在著ー個顯式接口成員執(zhí)行體,該執(zhí)行體與接口Interface及其成員Member相對應(yīng),則由它來實(shí)現(xiàn)Member成員。2、如果條件(1)不滿足,且C中存在著ー個非靜態(tài)的公有成員,該成員與接口成員Member相對應(yīng),則由它來實(shí)現(xiàn)Member成員。3、如果上述條件仍不滿足,則在類C定義的基類列表中尋找ー個C的基類D,用D來代替C。4、重復(fù)步驟,遍歷C的所有直接基類和非直接基類,直到找到ー個滿足條件的類的成員。5、如果仍然沒有找到,則報告錯誤。下面是ー個調(diào)用基類方法來實(shí)現(xiàn)接口成員的例子。類Class2實(shí)現(xiàn)了接口Interface"!,類Class2的基類Classi的成員也參與了接口的映射,也就是說類Class2在對接口Interfacel進(jìn)行實(shí)現(xiàn)時,使用了類Classi提供的成員方法F來實(shí)現(xiàn)接口Interfacel的成員方法F:interfaceInterfacel{voidF();)classClassi{publicvoidF(){}publicvoidG(){})classClass2:Classi,Interfacel{newpublicvoidG(){})注意:接口的成員包括它自己定義的成員,而且包括該接口所有父接口定義的成員。在接口映射時,不僅要對接口定義體中顯式定義的所有成員進(jìn)行映射,而且要對隱式地從父接口那里繼承來的所有接口成員進(jìn)行映射。在進(jìn)行接口映射時,還要注意下面兩點(diǎn):1、在決定由類屮的哪個成員來實(shí)現(xiàn)接口成員時,類屮顯式說明的接口成員比其它成員優(yōu)先實(shí)現(xiàn)。2、使用Private、protected和static修飾符的成員不能參與實(shí)現(xiàn)接口映射。例如:interfaceICIoneable{objectClone();)classC:ICIoneable{objectICIoneable.Clone(){...}publicobjectClone(){...})例子中成員ICIoneable.Clone稱為接口ICIoneable的成員Clone的實(shí)現(xiàn)者,因?yàn)樗秋@式說明的接口成員,比其它成員有著更高的優(yōu)先權(quán)。如果ー個類實(shí)現(xiàn)了兩個或兩個以上名字、類型和參數(shù)類型都相同的接口,那么類中的一個成員就可能實(shí)現(xiàn)所有這些接口成員:interfaceIControl{voidPaint();)interfaceIForm{voidPaint();}classPage:IControl,IForm{publicvoidPaint(){...}}這里,接口IControl和IForm的方法Paint都映射到了類Page中的Paint方法。當(dāng)然也可以分別用顯式的接口成員分別實(shí)現(xiàn)這兩個方法:interfaceIControl{voidPaint();}interfaceIForm{voidPaint();}classPage:IControl,IForm{publicvoidIControl.Paint(){〃具體的接口實(shí)現(xiàn)代碼}publicvoidIForm.Paint(){〃具體的接口實(shí)現(xiàn)代碼})上面的兩種寫法都是正確的。但是如果接口成員在繼承中覆蓋了父接口的成員,那么對該接口成員的實(shí)現(xiàn)就可能必須映射到顯式接口成員執(zhí)行體??聪旅娴睦?interface(Base{intP{get;})interfaceIDerived:IBase{newintP();)接口IDerived從接口IBase中繼承,這時接口IDerived的成員方法覆蓋了父接口的成員方法。因?yàn)檫@時存在著同名的兩個接口成員,那么對這兩個接口成員的實(shí)現(xiàn)如果不采用顯式接口成員執(zhí)行體,編譯器將無法分辨接口映射。所以,如果某個類要實(shí)現(xiàn)接口IDerived,在類中必須至少定義ー個顯式接口成員執(zhí)行體。采用ド面這些寫法都是合理的://-:對兩個接口成員都采用顯式接口成員執(zhí)行體來實(shí)現(xiàn)lassC:IDerived{intIBase.Pget{〃具體的接口實(shí)現(xiàn)代碼}intIDerived.P(){〃具體的接口實(shí)現(xiàn)代碼})〃二:對Ibase的接口成員釆用顯式接口成員執(zhí)行體來實(shí)現(xiàn)classC:IDerived{intIBase.Pget{//具體的接U實(shí)現(xiàn)代碼}publicintP(){〃具體的接口實(shí)現(xiàn)代碼))〃三:對IDerived的接口成員采用顯式接口成員執(zhí)行體來實(shí)現(xiàn)classC:IDerived{publicintPget{〃具體的接口實(shí)現(xiàn)代碼}intIDerived.P(){〃具體的接口實(shí)現(xiàn)代碼})另ー種情況是,如果一個類實(shí)現(xiàn)了多個接口,這些接口又擁有同一個父接口,這個父接口只允許被實(shí)現(xiàn)一次。usingSystem;interfaceIControl{voidPaint();interfaceITextBox:IControl{voidSetText(stringtext);)interfaceIListBox:IControl{voidSetltems(string[]items);)classComboBox:IControl,ITextBox,IListBox{voidIControl.Paint(){...}voidITextBox.SetText(stringtext){...}voidIListBox.Setltems(string[]items){...}}上面的例子中,類ComboBox實(shí)現(xiàn)了三個接U:IControLITex舊ox和IListBox。如果認(rèn)為ComboBox不僅實(shí)現(xiàn)了IControl接口,而且在實(shí)現(xiàn)!TextBox和IListBox的同時,乂分別實(shí)現(xiàn)了它們的父接口IControl。實(shí)際上,對接口ITextBox和!ListBox的實(shí)現(xiàn),分享了對接口IControl的實(shí)現(xiàn)。我們對C#的接口有了較全面的認(rèn)識,基本掌握了怎樣應(yīng)用〇#的接口編程,但事實(shí)上,C#的不僅僅應(yīng)用于?NET平臺,它同樣支持以前的COM,可以實(shí)現(xiàn)COM類到.NET類的轉(zhuǎn)換,如C#調(diào)用APL欲了解這方面的知識,請看下一節(jié)ー接口轉(zhuǎn)換。六、接口轉(zhuǎn)換C#中不僅支持.Net平臺,而且支持COM平臺-為了支持COM和?Net,C#包含ー種稱為屬性的獨(dú)特語言特性。ー個屬性實(shí)際上就是ー個C#類,它通過修飾源代碼來提供元信息。屬性使C#能夠支持特定的技術(shù),如COM和.Net,而不會干擾語言規(guī)范本身。C#提供將COM接口轉(zhuǎn)換為C#接口的屬性類?另ー些屬性類將COM類轉(zhuǎn)換為C#類。執(zhí)行這些轉(zhuǎn)換不需要任何IDL或類エ廠?,F(xiàn)在部署的任何COM組件都可以在接口轉(zhuǎn)換中使用。通常情況下,所需的調(diào)整是完全自動進(jìn)行的。特別是,可以使用運(yùn)行時可調(diào)用包裝(RCW)從.NET框架訪問COM組件。此包裝將COM組件提供的COM接ロ轉(zhuǎn)換為與.NET框架兼容的接口。對于OLE自動化接口,RCW可以從類型庫中自動生成;對于非OLE自動化接口,開發(fā)人員可以編寫自定義RCW,手動將COM接口提供的類型映射為與.NET框架兼容的類型。使用Comlmport引用COM組件COMInterop提供對現(xiàn)有COM組件的訪問,而不需要修改原始組件。使用Comlmport引用COM組件常包括ド面幾個方面的問題:1、創(chuàng)建COM對象。2、確定COM接口是否由對象實(shí)現(xiàn)。3、調(diào)用COM接口上的方法。4、實(shí)現(xiàn)可由COM客戶端調(diào)用的對象和接口。創(chuàng)建COM類包裝耍使C#代碼引用COM對象和接口,需要在C#中包含COM接口的定義。完成此操作的最簡單方法是使用Tlblnw.exe(類型庫導(dǎo)入程序),它是ー個包括在.NET框架SDK中的命令行工具。Tlblmp將COM類型庫轉(zhuǎn)換為.NET框架元數(shù)據(jù),從而有效地創(chuàng)建一個可以從任何托管語言調(diào)用的托管包裝。用Tlblmp創(chuàng)建的.NET框架元數(shù)據(jù)可以通過/R編譯器選項(xiàng)包括在C#內(nèi)部版本中。如果使用VisualStudio開發(fā)環(huán)境,則只需添加對COM類型庫的引用,將為您自動完成此轉(zhuǎn)換。Tlblmp執(zhí)行下列轉(zhuǎn)換:1、COMcoclass轉(zhuǎn)換為具有無參數(shù)構(gòu)造函數(shù)的C#類。COM結(jié)構(gòu)轉(zhuǎn)換為具有公共字段的C#結(jié)構(gòu)。檢查Tlblmp輸出的一種很好的方法是運(yùn)行.NET框架SDK命令行工具!ldasm.exe(Microsoft中間語言反匯編程序)來査看轉(zhuǎn)換結(jié)果.雖然Tlblmp是將COM定義轉(zhuǎn)換為C#的首選方法,但也不是任何時候都可以使用它(例如,在沒有COM定義的類型庫時或者Tlblmp無法處理類型庫中的定義時,就不能使用該方法)。在這些情況下,另?種方法是使用C#屬性在C#源代碼中手動定義COM定義。創(chuàng)建C#源映射后,只需編譯C#源代碼就可產(chǎn)生托管包裝。執(zhí)行COM映射需要理解的主要屬性包括:1、Comlmport:它將類標(biāo)記為在外部實(shí)現(xiàn)的COM類。2、Guid:它用于為類或接口指定通用唯一標(biāo)識符(UUID)o3、InterfaceType?它指定接口是從IUnknown還是從IDispatch派生。4、PreserveSig,它指定是否應(yīng)將本機(jī)返回值從HRESULT轉(zhuǎn)換為.NET框架異常。聲明COMcoclassCOMcoclass在C#中表示為類。這些類必須具有與其關(guān)聯(lián)的Comlmport屬性。下列限制適用于這些類:1、類不能從任何其他類繼承。2、類不能實(shí)現(xiàn)任何接口。4、類還必須具有為其設(shè)置全局唯一標(biāo)識符(GUID)的Guid屬性。以下示例在C#中聲明一個coclass://聲明個COM類FilgraphManager[Comlmport,Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]classFilgraphManager()C#編譯器將添加一個無參數(shù)構(gòu)造函數(shù),可以調(diào)用此構(gòu)造函數(shù)來創(chuàng)建COMcoclass的實(shí)例。創(chuàng)建COM對象COMcoclass在C#中表示為具有無參數(shù)構(gòu)造函數(shù)的類。使用new運(yùn)算符創(chuàng)建該類的實(shí)例等效于在C#中調(diào)用CoCreatelnstance。使用以上定義的類,就可以很容易地實(shí)例化此類:classMainClass(publicstaticvoidMain()(FilgraphManagerfilg=newFilgraphManager();))聲明COM接口COM接口在C#中表示為具有Comlmport和Guid屬性的接口。它不能在其基接口列表中包含任何接口,而且必須按照方法在COM接口中出現(xiàn)的順序聲明接口成員函數(shù)。在C#中聲明的COM接口必須包含其基接口的所有成員的聲明,IUnknown和IDispatch的成員除外(.NET框架將自動添加這些成員)。從IDispatch派生的COM接口必須用InterfaceType屬性予以標(biāo)記。從C#代碼調(diào)用COM接口方法時,公共語言運(yùn)行庫必須封送與COM對象之間傳遞的參數(shù)和返回值。對于毎個.NET框架類型均有一個默認(rèn)類型,公共語言運(yùn)行庫將使用此默認(rèn)類型在COM調(diào)用間進(jìn)行封送處理時封送。例如,C#字符串值的默認(rèn)封送處理是封送到本機(jī)類型LPTSTR(指向TCHAR字符緩沖區(qū)的指針)??梢栽贑OM接口的C#聲明中使用MarshalAs屬性重寫默認(rèn)封送處理。在COM中,返回成功或失敗的常用方法是返回一個HRESULT,并在MIDL中有一個標(biāo)記為“retval"、用于方法的實(shí)際返回值的out參數(shù)。在C#(和,NET框架)中,指示已經(jīng)發(fā)生錯誤的標(biāo)準(zhǔn)方法是引發(fā)異常。默認(rèn)情況ド,.NET框架為由其調(diào)用的COM接口方法在兩種異常處理類型之間提供自動映射。返回值更改為標(biāo)記為retval的參數(shù)的簽名(如果方法沒有標(biāo)記為retval的參數(shù),則為void)〇標(biāo)記為retval的參數(shù)從方法的參數(shù)列表中剝離。任何非成功返冋值都將導(dǎo)致引發(fā)System.COMException異常。此示例顯示用MIDL聲明的COM接口以及用C#聲明的同一接口(注意這些方法使用COM錯誤處理方法)。卜.面是接口轉(zhuǎn)換的C#程序:usingSystem.Runtime.InteropServices;〃聲明個COM接口!MediaControl[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),InterfaceType(ComlnterfaceType.lnterfacelsDual)]interfaceIMediaControl/Z這里不能列出任何基接口{voidRun();voidPause();voidStop();voidGetState([In]intmsTimeout,[Out]outintpfs);voidRenderFile([In,MarshalAs(UnmanagedType.BStr)]stringstrFilename);voidAddSourceFilter([In,MarshalAs(UnmanagedType.BStr)]stringstrFilename,[Out,MarshalAs(UnmanagedType.Interface)]outobjectppllnk);[return:MarshalAs(UnmanagedType.Interface)]objectFilterCollection();[return:MarshalAs(UnmanagedType.Interface)]objectRegFilterCollection();voidStopWhenReadyO;}若耍防止HRESULT翻譯為COMException,請?jiān)贑#聲明中將PreserveSig(true)屬性附加到方法。ド面是ー個使用C#映射媒體播放機(jī)COM對象的程序。程序清單2DemonCOM.csusingSystem;usingSystem.Runtime.InteropServices;namespaceQuartzTypeLib(〃聲明一個COM接口IMediaControl,此接口來源于媒體播放機(jī)COM類[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),Interface?ype(ComInterface?ype.lnterfacelsDual)]interfaceIMediaControl{〃列出接口成員voidRun();voidPause();voidStop();voidGetState([In]intms?imeout,[Out]outintpfs);voidRenderFile([In,MarshalAs(Unmanaged?ype.BStr)]stringstrFilename);voidAddSourceFilter([In,MarshalAs(Unmanaged?ype.BStr)]stringstrFilename,[Out,MarshalAs(Unmanaged?ype.Interface)]outobjectppUnk);[return:MarshalAs(Unmanaged?ype.Interface)]objectFilterCollection();[return:MarshalAs(Unmanaged?ype.Interface)]objectRegFilterCollection();voidStopWhenReadyO;}〃聲明一個COM類:[Comlmport,Guid(HE436EBB3-524F-11CE-9F53-0020AF0BA770")]classFilgraphManager〃此類不能再繼承其它基類或接口(〃這里不能有任何代碼,系統(tǒng)自動增加一個缺省的構(gòu)造函數(shù))}classMainClass(publicstaticvoidMain(string[]args){〃命令行參數(shù):if(args.Length!=1)(DisplayUsage();return;}Stringfilename=args[0];if(filename.Equals(V?H))Displayllsage();return;//聲明FilgraphManager的實(shí)類對象:QuartzTypeLib.FilgraphManagergraphManager=newQuartzTypeLib.FilgraphManager();〃聲明IMediaControl的實(shí)類對象::QuartzTypeLib.IMediaControlme=(QuartzTypeLib.lMediaControl)graphManager;/Z調(diào)用COM的方法:mc.RenderFile(filename);〃運(yùn)行文件.mc.Run();〃暫借停.Console.WriteLine("PressEntertocontinue.*');Console.ReadLine();)privatestaticvoidDisplayUsage(){/Z顯示Console.WriteLine("媒體播放機(jī):播放AVI文件.”);Console.WriteLine("使用方法:VIDEOPLAYER.EXE文件名”);)}運(yùn)行示例:若要顯示影片示例Clock.avi?請使用以下命令:interop2%windir%\clock.avi這將在屏幕上顯示影片,直到按ENTER鍵停止。在.NET框架程序中通過DHImport使用Win32API.NET框架程序可以通過靜態(tài)DLL入口點(diǎn)的方式來訪問木機(jī)代碼庫。Dlllmport屬性用于指定包含外部方法的實(shí)現(xiàn)的dll位置。Dlllmport屬性定義如ド:namespaceSystem.Runtime.InteropServices([AttributeUsage(AttributeTargets.Method)]publicclassDIIImportAttribute:System.Attribute{publicDlllmportAttribute(stringdllName){...}publicCallingConventionCallingconvention;publicCharSetCharSet;publicstringEntryPoint;publicboolExactSpelling;publicboolPreserveSig;publicboolSetLastError;publicstringValue{get{...}}})說明:1、Dlllmport只能放置在方法聲明上。2、Dlllmport具有單個定位參數(shù):指定包含被導(dǎo)入方法的dll名稱的dllName參數(shù)。3、Dlllmport具有五個命名參數(shù):a、Callingconvention參數(shù)指示入口點(diǎn)的調(diào)用約定。如果未指定Callingconvention,則使用默認(rèn)值CallingConvention.Winapi〇b、CharSet參數(shù)指示用在入口點(diǎn)中的字符集。如果未指定CharSet,則使用默認(rèn)值CharSet.Auto〇c、EntryPoint參數(shù)給出dl(中入口點(diǎn)的名稱。如果未指定EntryPoint?則使用方法本身的名稱。d、ExactSpelling參數(shù)指示EntryPoint是否必須與指示的入口點(diǎn)的拼寫完全匹配。如果未指定ExactSpelling?則使用默認(rèn)值false〇e、PreserveSig參數(shù)指示方法的簽名應(yīng)當(dāng)被保留還是被轉(zhuǎn)換。當(dāng)簽名被轉(zhuǎn)換時,它被轉(zhuǎn)換為?個具有HRESULT返回值和該返回值的一個名為retval的附加輸出參數(shù)的簽名。如果未指定PreserveSig,則使用默認(rèn)值true〇f、SetLastError參數(shù)指示方法是否保留Win32”ヒー錯誤,如果未指定SetLastError,則使用默認(rèn)值false。4、它是一次性屬性類。5、此外,用Dlllmport屬性修飾的方法必須具有extern修飾符。下面是C#調(diào)用Win32MessageBox函數(shù)的示例:usingSystem;usingSystem.Runtime.InteropServices;classMainApp{〃通過Dlllmport弓I用user32.dl!類。MessageBox來自于user32.dl!類[Dlllmport(Muser32.dir,EntryPoint="MessageBox")]publicstaticexternintMessageBox(inthWnd,StringstrMessage,StringstrCaption,uintuiType);publicstaticvoidMain()(MessageBox(〇,”您好,這是Plnvoke!",".NET",0);))面向?qū)ο蟮木幊陶Z言幾乎都用到了抽象類這ー概念,抽象類為實(shí)現(xiàn)抽象事物提供了更大的靈活性。C#也不例外,C#通過覆蓋虛接口的技術(shù)深化了抽象類的應(yīng)用。欲了解這方面的知識,請看ドー節(jié)ー覆蓋虛接口七、覆蓋虛接口有時候我們需要表達(dá)一ー種抽象的東西,它是ー些東西的概括,但我們又不能真正的看到它成為ー個實(shí)體在我們眼前出現(xiàn),為此面向?qū)ο蟮木幊陶Z言便有了抽象類的概念。C#作為ー個面向?qū)ο蟮恼Z言,必然也會引入

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論