類別介面簡介
更新: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。