共用方式為


類別介面簡介

更新:2007 年 11 月

沒有在 Managed 程式碼中明確定義的類別介面,是一種會公開所有在 .NET 物件上明確公開之公用方法、屬性、欄位和事件的介面。這個介面可以是雙重介面 (Dual Interface),也可以是分派 (Dispatch-only) 介面。類別介面採用了 .NET 類別本身的名稱,前面再加上一個底線。例如,對於類別 Mammal,其類別介面為 _Mammal。

對於衍生類別 (Derived Class),類別介面也會公開基底類別的所有公用方法、屬性和欄位。衍生類別也會公開每一基底類別的類別介面。舉例來講,如果類別 Mammal 擴充了類別 MammalSuperclass,而它本身又擴充了 System.Object,那麼 .NET 物件會向 COM 用戶端公開三個類別介面,分別為 _Mammal、_MammalSuperclass 和 _Object。

例如,請考量下列 .NET 類別:

' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
    Sub Eat()
    Sub Breathe()
    Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
    void  Eat();
    void  Breathe():
    void  Sleep();
}

COM 用戶端可以取得名為 _Mammal 的類別介面指標,這個指標在型別程式庫匯出工具 (Tlbexp.exe) 所產生的型別程式庫中有相關描述。如果 Mammal 類別實作了一個或多個介面,那麼這些介面將會出現在 Coclass 之下。

   [odl, uuid(…), hidden, dual, nonextensible, oleautomation]
   interface _Mammal : IDispatch
   {
       [id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
           pRetVal);
       [id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
           VARIANT_BOOL* pRetVal);
       [id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
       [id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
       [id(0x6002000d)] HRESULT Eat();
       [id(0x6002000e)] HRESULT Breathe();
       [id(0x6002000f)] HRESULT Sleep();
   }
   [uuid(…)]
   coclass Mammal 
   {
       [default] interface _Mammal;
   }

產生類別介面是選擇性的。根據預設,COM Interop 會對您匯出到型別程式庫的每一類別,產生一個唯分派介面。您可以將 ClassInterfaceAttribute 套用到您的類別,來防止或修改這個介面的自動建立。雖然類別介面可以減輕向 COM 公開 Managed 類別的工作,不過它的運用也很有限。

警告:

使用類別介面 (非自行明確定義),可能會讓將來 Managed 類別的版本控制變得很複雜。使用類別介面前,請先閱讀以下指導原則。

定義供 COM 用戶端使用的明確介面,而不產生類別介面。

因為 COM Interop 會自動產生類別介面,所以在類別版本確定之後的變更,可能會改變由 Common Language Runtime 所公開之類別介面的配置。由於 COM 用戶端通常並未預期要處理介面配置的變更,因此,如果您變更類別的成員配置,它們可能會中斷。

這項指導原則在強調一個概念,向 COM 用戶端公開的介面必須維持不變。為了降低因無意間重新安排介面配置而造成 COM 用戶端中斷的風險,請明確定義介面,將所有對類別的變更隔離於介面配置之外。

使用 ClassInterfaceAttribute 來解除類別介面的自動產生,並且實作這個類別的明確介面,如以下程式碼片段所示:

<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
    Implements IExplicit
    Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit {
    void M();
}

ClassInterfaceType.None 值會防止在類別中繼資料匯出至型別程式庫時產生類別介面。在前面的範例中,COM 用戶端只透過 IExplicit 介面存取 LoanApp 類別。

避免快取分派識別項 (DispId)。

對於指令化的用戶端、Microsoft Visual Basic 6.0 用戶端或任何不快取介面成員之 DispId 的晚期繫結用戶端,使用類別介面是可以接受的選項。DispId 可以識別介面成員以啟用晚期繫結。

對於類別介面,DispId 的產生是依據介面中成員的位置。如果您變更了成員的順序並且將這個類別匯出至型別程式庫,您將會改變在類別介面中產生的 DispId。

若要避免在使用類別介面時中斷晚期繫結 COM 用戶端,請以 ClassInterfaceType.AutoDispatch 值套用 ClassInterfaceAttribute。這個值會實作唯分派類別介面,但會從型別程式庫略去介面描述。沒有介面描述,用戶端就不能在編譯期間快取 DispId。雖然這是類別介面的預設介面型別,但是您可以明確地套用這個屬性值。

<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
    Implements IAnother
    Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch]
public class LoanApp : IAnother {
    void M();
}

若要在執行階段時取得介面成員的 DispId,COM 用戶端可以呼叫 IDispatch.GetIdsOfNames。若要在介面上叫用 (Invoke) 方法,請將傳回的 DispId 當做 IDispatch.Invoke 的引數傳遞。

對類別介面限制使用雙重介面選項。

雙重介面可以啟用由 COM 用戶端早期和晚期繫結至介面成員。在設計階段和測試當中,您可能會發現將類別介面設定為雙重介面很有用處。對於永遠不會被修改的 Managed 類別 (及其基底類別),這個選項也是可以接受的。在其他的所有情況中,則應避免將類別介面設定為雙重介面。

自動產生的雙重介面在一些極少見的情況中可能很適合;但是,它也時常會產生版本相關的複雜性。例如,使用衍生類別之類別介面的 COM 用戶端,可能很容易在基底類別變更時中斷。當第三者提供了基底類別時,類別介面的配置就不是您所能控制的了。此外,與唯分派介面的不同之處在於,雙重介面 (ClassInterface.AutoDual) 會在匯出的型別程式庫中提供類別介面的描述。這種描述有助於晚期繫結用戶端在 Run Time 時快取 DispId。

請參閱

概念

COM 可呼叫包裝函式

限定互通的 .NET 型別

參考

ClassInterfaceAttribute