Mieux vaut écrire votre bibliothèque .NET avec les limitations COM à l'esprit, ou séparer votre bibliothèque .NET d'Interop?


9

Je suis tombé sur cet article intéressant: How I Came to Love COM Interoperability on CodeProject, qui m'a fait réfléchir ...

L'auteur soutient qu'ils ne veulent pas de COM-ities dans leur bibliothèque .NET car cela enlève à la beauté de leur bibliothèque .NET. Au lieu de cela, ils préfèrent écrire une bibliothèque Interop distincte qui expose leur bibliothèque .NET à COM. Cette bibliothèque Interop gérerait le fait que COM ne prend pas en charge les constructeurs avec paramètres, méthodes surchargées, génériques, héritage, méthodes statiques, etc.

Et même si je pense que c'est très nouveau, cela ne complique-t-il pas simplement le projet?

  • Vous devez maintenant tester à l'unité votre bibliothèque .NET ET une bibliothèque Interop.
  • Vous devez maintenant passer du temps à découvrir comment contourner votre belle bibliothèque .NET et l'exposer à COM.
  • Vous devez, effectivement, doubler ou tripler votre nombre de classes.

Je peux certainement comprendre si vous avez besoin de votre bibliothèque pour prendre en charge COM et non-COM. Cependant, si vous avez l'intention d'utiliser COM uniquement, ce type de conception apporte-t-il des avantages que je ne vois pas? Bénéficiez-vous uniquement des avantages du langage C #?

Ou, cela facilite-t-il la gestion des versions de votre bibliothèque en vous fournissant un wrapper? Est-ce que cela accélère l'exécution de vos tests unitaires en ne nécessitant pas l'utilisation de COM?


2
Vous voulez dire, en plus de pouvoir utiliser des outils utiles pour améliorer / nettoyer le vrai code? Et fournir un chemin de migration clair pour ce truc COM (vraisemblablement hérité)?
Telastyn

3
C'est comme un motif de façade écrit en grand sur un espace de noms entier. Je pense qu'il est intelligent de l'organiser comme ceci si vous souhaitez utiliser les fonctionnalités .NET mais avez vraiment besoin d'OLE Automation. Vos wrappers COM seront essentiellement destinés à convertir des idiomes modernes (immuables? Génériques?) En objets COM plus anciens avec des constructeurs sans paramètres, des objets fortement mutables et des SAFEARRAY pour vos listes génériques. Si vous prenez en charge d'autres composants .NET ou êtes totalement amoureux des nouveaux styles, cela aurait du sens. Si vous ne supportez que COM, alors pourquoi ne pas écrire le tout en VB6 (32 bits) et ne garder qu'un seul idiome?
Mike

3
@MasonWheeler: Il est obsolète de la même manière que tous les logiciels de plus de 6 mois sont "hérités", ai-je raison?
whatsisname

2
@MasonWheeler COM ne peut pas être déprécié quel que soit le nombre de personnes (y compris certains groupes au sein de Microsoft) qui le souhaiteraient car c'est toujours le meilleur choix pour contrôler les choses hors processus.
Mike

2
@Mike, je cherche en fait à porter un projet VB6 sur C # .NET. Les API utilisateur que nous utilisons sont très simples, cependant, le code en arrière-plan n'est pas maintenable tel quel. J'espère implémenter la majorité de la bibliothèque en C # .NET et fournir une façade d'interopérabilité qui fournit les API simples qui interagissent avec les différentes classes.
robodude666

Réponses:


7

Cependant, si vous avez l'intention d'utiliser COM uniquement, ce type de conception apporte-t-il des avantages que je ne vois pas?

Cet extrait de votre question est la partie la plus importante de votre décision de conception ici, et la réponse est (comme toujours) cela dépend .

Si vous avez uniquement l'intention d'utiliser la bibliothèque via COM Interop, vous devez concevoir l'ensemble du système dans cet esprit. Il n'y a aucune raison de tripler le nombre de lignes. Plus il y a de LoC dans le système, plus il aura de défauts. Vous devez donc concevoir votre système pour n'utiliser que des fonctionnalités compatibles COM.

Cependant , je ne peux pas penser à une bonne raison de restreindre l'utilisation d'une bibliothèque .Net à COM. Pourquoi ne voudriez- vous pas pouvoir utiliser l'assembly dans un autre code .Net? Cela n'a aucun sens de vous limiter de cette façon.

J'ai une certaine expérience avec cela, donc je vais partager comment je l'ai géré. Ce n'est certainement pas idéal. J'ai fait des compromis. J'utilise uniquement une façade pour les classes COM (interfaces vraiment) qui sont incompatibles avec les nouveaux idiomes .Net, sinon, j'expose directement l'interface .Net à COM. Cela signifie essentiellement que j'utilise une façade pour toute interface utilisant des génériques. Les génériques sont la seule chose que j'ai rencontrée qui sont tout simplement incompatibles. Malheureusement, cela signifie une façade pour tout ce qui renvoie un IEnumerable<T>, ce qui est assez courant.

Cependant, ce n'est pas aussi mauvais qu'il n'y paraît, car votre classe de façade hérite de votre classe .Net et implémente une interface Interop spécifique. Quelque chose comme ça.

namespace Company.Product.Feature
{
    public class MyClass : INetInterface 
    {
        // lots of code
    }
}

namespace Company.Product.Interop
{
    public class MyClass : Feature.MyClass, IComInterface
    {
        // over ride only the  incompatible properties/methods
    }
}

Cela maintient votre code en double à un minimum absolu et protège votre interface COM contre les modifications "non intentionnelles". Au fur et à mesure que votre classe principale / interface change, votre classe d'adaptateur doit remplacer d'autres méthodes (ou casser l'interface et augmenter le numéro de version principal). En revanche, tout votre code Interop n'est pas stocké dans l' Interopespace de noms, ce qui peut conduire à une organisation des fichiers déroutante. Comme je l'ai dit, c'est un compromis.

Pour un exemple complet de cela, vous pouvez parcourir les dossiers SourceControl et Interop de mon référentiel. ISourceControlProvideret GitProvidersera d'un intérêt particulier.


Merci pour la réponse @RubberDuck! J'ai traversé le projet RubberDuck il y a quelques jours; la lecture du code a été très intéressante. Pouvez-vous passer IEnumerableà VBA? De plus, dans le Rubberduck.Interop.GitProviderpourquoi avez-vous défini des constructeurs paramétrés si COM ne les prend pas en charge?
robodude666

Oui, vous pouvez transmettre un IEnumerableCOM via, mais il doit s'agir de la version non générique plus ancienne. Quant aux constructeurs, jetez un œil au SourceControlClassFactory. Fondamentalement, vous le faîtes en initialisant une instance d'une classe qui n'a pas besoin de paramètres pour son ctor et en l'utilisant pour accéder à de nouvelles instances des classes de votre API.
RubberDuck

Je suppose que tout ce qui est défini dans vos ComVisible(true)classes / interfaces que COM ne prend pas en charge est simplement "ignoré"? Je suppose également que si vous avez une classe du même nom définie dans plusieurs espaces de noms, vous devrez fournir un unique ProgIdsi vous souhaitez exposer plusieurs d'entre eux?
robodude666

Oui. C'est vrai et les staticméthodes sont ignorées. J'essaie de voir s'il y a un équivalent de VB6 VB_PredeclaredIddisponible. Je pense qu'il pourrait être possible de créer une instance par défaut.
RubberDuck

7

il enlève à la beauté de leur bibliothèque .NET

Cet auteur a besoin d'une gifle de réveil au visage. Pour tout projet professionnel, l'objectif est de créer des logiciels de travail que les gens souhaitent utiliser. Tout type d'idéaux malavisés sur la beauté vient bien après. Si tel est leur seul raisonnement, l'auteur a perdu toute crédibilité.

Pouvoir marquer vos classes comme com-visibles et pouvoir parler à votre code .NET via COM est extrêmement utile. Jeter ce grand avantage à la productivité pour la beauté est fou.

Cependant, faire fonctionner les choses avec COM nécessite des compromis, nécessitant un constructeur sans argument est l'un d'entre eux, et certaines des autres choses que vous avez mentionnées. Si ce sont des fonctionnalités que vous n'utilisez pas de toute façon, ou si cela ne vous fera pas de mal de ne pas les utiliser, alors il y a beaucoup de travail à enregistrer en utilisant la même classe pour COM et non com.

D'un autre côté, si vos clients COM attendent une interface radicalement différente, ont des dépendances ou d'autres limitations qui sont désagréables à gérer dans vos classes .NET, ou si vous vous attendez à changer considérablement vos classes .NET et devez maintenir COM en arrière- compatibilité, puis créez par tous les moyens une classe Interop pour combler cet écart.

Mais ne pensez pas à la "beauté". Être en mesure d'exposer COM via .NET est censé vous faire économiser du travail. Ne créez pas plus de travail pour vous-même.


+1. Bien que j'aime la beauté des boutons noirs sur une boîte noire, les aspects pratiques sont beaucoup plus importants.
gbjbaanb

En fait, la personne qui a introduit le terme «beauté» dans cette discussion pourrait avoir besoin d'une gifle de réveil, et ce n'est pas l'auteur de cet autre article.
Doc Brown

2
Juste une note latérale, si vos constructeurs actuels nécessitent des arguments, il est préférable de fournir une fabrique de classe pour vos composants COM, plutôt que de repenser l'interface / API existante.
RubberDuck

1
Est-il possible d'exposer votre usine en tant que classe "GlobalMultiUse"? Vous pouvez donc simplement vous Set oObject = MyCOMInterop.NewMyClass()passer de l'instanciation préalable d'un nouvel objet Factory?
robodude666

C'est une sacrément bonne question @ robodude666. Maintenant que vous en parlez, je l' espère .
RubberDuck

3

Eh bien, pour être juste, l'auteur original de cet article n'a utilisé le mot "beauté" nulle part dans son article, c'était vous, ce qui pourrait donner une impression biaisée à ceux qui n'ont pas pris le temps de lire l'article par se.

Pour autant que j'ai compris cet article, la bibliothèque .NET existait déjà et a été écrite sans COM à l'esprit - probablement parce qu'au moment où la bibliothèque a été créée, il n'était pas nécessaire de prendre en charge COM.

Plus tard, l'exigence supplémentaire de prise en charge de COM a été introduite. Face à une telle situation, vous avez deux options:

  • refonte de la lib originale pour la rendre compatible avec COM interop (avec le risque de casser des choses)

  • laissez la bibliothèque de travail d'origine la plupart du temps telle qu'elle est, et créez plutôt un assemblage séparé avec la fonction d'un "wrapper" (ou "adaptateur")

La création d'emballages ou d'adaptateurs est un modèle de conception bien connu (ou dans ce cas plus un modèle architectural), et les avantages et les inconvénients sont également bien connus. Un argument en est la meilleure "séparation des préoccupations", et cela n'a rien à voir avec la beauté.

À vos préoccupations

Vous devez maintenant tester à l'unité votre bibliothèque .NET ET une bibliothèque Interop.

Lorsque vous introduisez votre interface COM dans votre bibliothèque .NET existante par refonte, vous devez tester cette interface non plus. L'introduction d'un adaptateur écrit manuellement peut nécessiter des tests supplémentaires sur le fonctionnement de l'interface, bien sûr, mais il n'est pas nécessaire de copier tous les tests unitaires de la fonctionnalité métier principale de la bibliothèque. N'oubliez pas que c'est juste une autre interface pour la lib.

Vous devez maintenant passer du temps à découvrir comment contourner votre belle bibliothèque .NET et l'exposer à COM.

Lorsque vous devez repenser la bibliothèque d'origine, vous devez également le comprendre, et je suppose que ce sera beaucoup plus de travail.

Vous devez, effectivement, doubler ou tripler votre nombre de classes.

Uniquement pour les classes qui sont exposées via l'interface COM publique, qui devrait être un petit nombre de classes faisant partie de la bibliothèque d'origine.

Dit que, pour une situation différente que vous avez mentionnée, alors que vous savez déjà que vous devez prendre en charge COM en tant que citoyen de première classe dès le début, je pense que vous avez raison: il faut éviter d'avoir à ajouter une bibliothèque d'adaptateurs séparés et à concevoir le Lib .NET avec prise en charge COM directe.


(+1) Cependant, "... qui devrait être un petit nombre de ... la bibliothèque d'origine" n'est que parfois vrai. Il existe de nombreux types de projets conçus dans le "style bibliothèque de classes", où une classe équivaut à un objectif publiquement visible et que les classes composent pour donner lieu à des fonctionnalités intéressantes. Dans ce cas, il pourrait y avoir un mappage un à un entre le nombre de classes .NET et le nombre de classes COM interop.
rwong
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.