Introducing the Class Interface

Introducing the Class Interface

The class interface, which is not explicitly defined in managed code, is an interface that exposes all public methods, properties, fields, and events that are explicitly exposed on the .NET object. This interface can be a dual or dispatch-only interface. The class interface receives the name of the .NET class itself, preceded by an underscore. For example, for class Mammal, the class interface is _Mammal.

For derived classes, the class interface also exposes all public methods, properties, and fields of the base class. The derived class also exposes a class interface for each base class. For example, if class Mammal extends class MammalSuperclass, which itself extends System.Object, the .NET object exposes to COM clients three class interfaces named _Mammal, _MammalSuperclass, and _Object.

For example, consider the following .NET class:

[Visual Basic]
' 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
[C#]// 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();
}

The COM client can obtain a pointer to a class interface named _Mammal, which is described in the type library that the Type Library Exporter (Tlbexp.exe) tool generates. If the Mammal class implemented one or more interfaces, the interfaces would appear under the 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;
   }

Generating the class interface is optional. By default, COM interop generates a dispatch-only interface for each class you export to a type library. You can prevent or modify the automatic creation of this interface by applying the ClassInterfaceAttribute to your class. Although the class interface can ease the task of exposing managed classes to COM, its uses are limited.

CAUTION   Using the class interface, instead of explicitly defining your own, can complicate the future versioning of your managed class. Please read the following guidelines before using the class interface.

Define an explicit interface for COM clients to use rather than generating the class interface.

Because COM interop generates a class interface automatically, post-version changes to your class can alter the layout of the class interface exposed by the common language runtime. Since COM clients are typically unprepared to handle changes in the layout of an interface, they break if you change the member layout of the class.

This guideline reinforces the notion that interfaces exposed to COM clients must remain unchangeable. To reduce the risk of breaking COM clients by inadvertently reordering the interface layout, isolate all changes to the class from the interface layout by explicitly defining interfaces.

Use the ClassInterfaceAttribute to disengage the automatic generation of the class interface and implement an explicit interface for the class, as the following code fragment shows:

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

The ClassInterfaceType.None value prevents the class interface from being generated when the class metadata is exported to a type library. In the preceding example, COM clients can access the LoanApp class only through the IExplicit interface.

Avoid caching dispatch identifiers (DispIds).

Using the class interface is an acceptable option for scripted clients, Microsoft Visual Basic 6.0 clients, or any late-bound client that does not cache the DispIds of interface members. DispIds identify interface members to enable late binding.

For the class interface, generation of DispIds is based on the position of the member in the interface. If you change the order of the member and export the class to a type library, you will alter the DispIds generated in the class interface.

To avoid breaking late-bound COM clients when using the class interface, apply the ClassInterfaceAttribute with the ClassInterfaceType.AutoDispatch value. This value implements a dispatch-only class interface, but omits the interface description from the type library. Without an interface description, clients are unable to cache DispIds at compile time. Although this is the default interface type for the class interface, you can apply the attribute value explicitly.

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

To get the DispId of an interface member at run time, COM clients can call IDispatch.GetIdsOfNames. To invoke a method on the interface, pass the returned DispId as an argument to IDispatch.Invoke.

Restrict using the dual interface option for the class interface.

Dual interfaces enable early and late binding to interface members by COM clients. At design time and during testing, you might find it useful to set the class interface to dual. For a managed class (and its base classes) that will never be modified, this option is also acceptable. In all other cases, avoid setting the class interface to dual.

An automatically generated dual interface might be appropriate in rare cases; however, more often it creates version-related complexity. For example, COM clients using the class interface of a derived class can easily break with changes to the base class. When a third party provides the base class, the layout of the class interface is out of your control. Further, unlike a dispatch-only interface, a dual interface (ClassInterface.AutoDual) provides a description of the class interface in the exported type library. Such a description encourages late-bound clients to cache DispIds at run time.

posted on 2009-12-26 15:59  Acor  阅读(336)  评论(0编辑  收藏  举报

导航