Présentation de l'interface de classe
L'interface de classe, qui n'est pas définie explicitement dans le code managé, est une interface qui expose tous les événements, champs, propriétés et méthodes publics qui sont exposés explicitement sur l'objet .NET. Cette interface peut être une interface double ou de dispatch uniquement. L'interface de classe reçoit le nom de la classe .NET elle-même, précédé d'un trait de soulignement. Par exemple, pour la classe Mammal, l'interface de classe est _Mammal.
Pour les classes dérivées, l'interface de classe expose également tous les champs, méthodes et propriétés publics de la classe de base. La classe dérivée expose aussi une interface de classe pour chaque classe de base. Par exemple, si la classe Mammal étend la classe MammalSuperclass, qui elle-même étend System.Object, l'objet .NET expose aux clients COM trois interfaces de classe nommées _Mammal, _MammalSuperclass et _Object.
Examinons, par exemple, la classe .NET suivante :
' 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();
}
Le client COM peut obtenir un pointeur désignant une interface de classe nommée _Mammal, qui est décrite dans la bibliothèque de types générée par l'outil Type Library Exporter (Tlbexp.exe). Si la classe Mammal implémente une ou plusieurs interfaces, les interfaces apparaissent sous la coclasse.
[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;
}
La génération de l'interface de classe est facultative. Par défaut, COM Interop génère une interface de dispatch uniquement pour chaque classe que vous exportez vers une bibliothèque de types. Vous pouvez empêcher ou modifier la création automatique de cette interface en ClassInterfaceAttribute à votre classe. Bien que l'interface de classe simplifie la tâche d'exposition des classes managées pour COM, ses utilisations sont limitées.
Attention |
---|
Si vous utilisez l'interface de classe, au lieu de définir explicitement la vôtre, vous risquez de compliquer le versioning futur de votre classe managée.Veillez à prendre connaissance des indications suivantes avant d'utiliser l'interface de classe. |
Définissez une interface explicite à utiliser par les clients COM au lieu de générer l'interface de classe.
Sachant que COM Interop génère automatiquement une interface de classe, les modifications post-version effectuées dans votre classe peuvent modifier la disposition de l'interface de classe exposée par le Common Language Runtime. Dans la mesure où les clients COM ne sont généralement pas préparés à gérer les modifications opérées dans la disposition d'une interface, ils s'interrompent en cas de modification de la disposition des membres de la classe.
Cette indication renforce la notion selon laquelle les interfaces exposées aux clients COM doivent rester intactes. Pour réduire le risque d'interruption des clients COM à la suite d'une réorganisation accidentelle de la disposition de l'interface, isolez toutes les modifications apportées à la classe par rapport à la disposition de l'interface en définissant explicitement les interfaces.
Utilisez ClassInterfaceAttribute pour désactiver la génération automatique de l'interface de classe et implémentez une interface explicite de la classe, comme le montre le fragment de code suivant :
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit {
void M();
}
La valeur ClassInterfaceType.None empêche la génération de l'interface de classe lorsque les métadonnées de classe sont exportées vers une bibliothèque de types. Dans l'exemple précédent, les clients COM peuvent accéder à la classe LoanApp uniquement via l'interface IExplicit.
Évitez de mettre en cache des identificateurs de dispatch (DispId).
L'utilisation de l'interface de classe est une option acceptable pour les clients faisant l'objet de scripts, les clients Microsoft Visual Basic 6.0 ou tout client à liaison tardive qui ne met pas en cache les DispId des membres d'interface. Les DispId identifient les membres d'interface afin d'activer la liaison tardive.
Pour l'interface de classe, la génération d'identificateurs DispId repose sur la position du membre dans l'interface. Si vous modifiez l'ordre du membre et exportez la classe vers une bibliothèque de types, vous modifierez les DispId générés dans l'interface de classe.
Pour éviter d'interrompre les clients COM à liaison tardive lorsque vous utilisez l'interface de classe, appliquez ClassInterfaceAttribute avec la valeur ClassInterfaceType.AutoDispatch. Cette valeur implémente une interface de classe de dispatch uniquement, mais omet la description de l'interface dans la bibliothèque de types. Sans description d'interface, les clients ne peuvent pas mettre en cache les DispId au moment de la compilation. Bien qu'il s'agisse du type interface par défaut pour l'interface de classe, vous pouvez appliquer la valeur de l'attribut de façon explicite.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch]
public class LoanApp : IAnother {
void M();
}
Pour obtenir le DispId d'un membre d'interface au moment de l'exécution, les clients COM peuvent appeler IDispatch.GetIdsOfNames. Pour appeler une méthode sur l'interface, passez le DispId retourné comme argument à IDispatch.Invoke.
Limitez l'utilisation de l'option d'interface double pour l'interface de classe.
Les interfaces doubles permettent la liaison anticipée et tardive des clients COM aux membres d'interface. Au moment du design ou pendant les tests, vous pouvez trouver pratique de définir l'interface de classe comme étant de type double. Pour une classe managée (et ses classes de base) qui ne sera jamais modifiée, cette option est également acceptable. Dans tous les autres cas, évitez de définir l'interface de classe comme étant de type double.
Une interface double générée automatiquement peut être appropriée dans quelques rares cas ; cependant, le plus souvent elle complique la gestion des versions. Par exemple, les clients COM utilisant l'interface de classe d'une classe dérivée peuvent facilement s'interrompre en cas de modifications apportées à la classe de base. Quand la classe de base est fournie par un tiers, la disposition de l'interface de classe n'est plus sous votre contrôle. De plus, contrairement à une interface de dispatch uniquement, une interface double (ClassInterface.AutoDual) fournit une description de l'interface de classe dans la bibliothèque de types exportée. Une telle description encourage les clients à liaison tardive à mettre en cache les DispId au moment de l'exécution.