Schreiben Sie Ihre .NET-Bibliothek besser unter Berücksichtigung der COM-Einschränkungen oder trennen Sie Ihre .NET-Bibliothek von Interop?

9

Ich bin auf diesen interessanten Artikel gestoßen : Wie ich die COM-Interoperabilität auf CodeProject liebte, was mich zum Nachdenken brachte ...

Der Autor argumentiert, dass sie keine COM-ities in ihrer .NET-Bibliothek haben möchten, da dies die Schönheit ihrer .NET-Bibliothek beeinträchtigt. Stattdessen schreiben sie lieber eine separate Interop-Bibliothek, die ihre .NET-Bibliothek für COM verfügbar macht. Diese Interop-Bibliothek würde die Tatsache behandeln, dass COM keine Konstruktoren mit Parametern, überladenen Methoden, Generika, Vererbung, statischen Methoden usw. unterstützt.

Und obwohl ich denke, dass das sehr neu ist, schließt es das Projekt nicht einfach ab?

  • Jetzt müssen Sie Ihre .NET-Bibliothek UND eine Interop-Bibliothek einem Komponententest unterziehen.
  • Jetzt müssen Sie Zeit damit verbringen, herauszufinden, wie Sie Ihre schöne .NET-Bibliothek umgehen und sie COM zugänglich machen können.
  • Sie müssen Ihre Klassenanzahl effektiv verdoppeln oder verdreifachen.

Ich kann sicher verstehen, ob Sie Ihre Bibliothek benötigen, um sowohl COM als auch Nicht-COM zu unterstützen. Wenn Sie jedoch nur COM verwenden möchten, bringt diese Art von Design Vorteile, die ich nicht sehe? Nutzen Sie nur die Vorteile der C # -Sprache?

Oder erleichtert es die Versionierung Ihrer Bibliothek, indem es Ihnen einen Wrapper zur Verfügung stellt? Werden Ihre Unit-Tests dadurch schneller ausgeführt, ohne dass COM erforderlich ist?

robodude666
quelle
2
Sie meinen, neben der Möglichkeit, nützliche Tools zu verwenden, um den echten Code besser / sauberer zu machen? Und einen klaren Migrationspfad für dieses (vermutlich ältere) COM-Zeug bereitstellen?
Telastyn
3
Dies ist wie ein Fassadenmuster, das über einen ganzen Namespace groß geschrieben wird. Ich denke, es ist klug, es so zu organisieren, wenn Sie .NET-Funktionen verwenden möchten, aber wirklich OLE-Automatisierung benötigen. Ihre COM-Wrapper bestehen im Wesentlichen darin, moderne (unveränderliche? Generische?) Redewendungen in ältere COM-Objekte mit parameterlosen Konstruktoren, stark veränderlichen Objekten und SAFEARRAYs für Ihre generischen Listen umzuwandeln. Wenn Sie andere .NET-Komponenten unterstützen oder sich total in die neuen Stile verlieben, ist dies sinnvoll. Wenn Sie NUR COM unterstützen, warum schreiben Sie dann nicht das Ganze in VB6 (32 Bit) und behalten nur eine Redewendung bei?
Mike unterstützt Monica
3
@MasonWheeler: Es ist auf die gleiche Weise veraltet, wie jede Software, die älter als 6 Monate ist, "Legacy" ist, habe ich Recht?
Whatsisname
2
@MasonWheeler COM kann nicht abgeschrieben werden, egal wie viele Personen (einschließlich einiger Gruppen innerhalb von Microsoft) dies wünschen, da es immer noch die beste Wahl ist, um Dinge außerhalb des Prozesses zu steuern.
Mike unterstützt Monica
2
@ Mike, ich möchte gerade ein VB6-Projekt nach C # .NET portieren. Die von uns verwendeten benutzerbezogenen APIs sind sehr einfach, der Code hinter den Kulissen kann jedoch nicht unverändert gewartet werden. Ich hoffe, einen Großteil der Bibliothek in C # .NET implementieren zu können und eine Interop-Fassade bereitzustellen, die die einfachen APIs bereitstellt, die mit den verschiedenen Klassen interagieren.
Robodude666

Antworten:

7

Wenn Sie jedoch nur COM verwenden möchten, bringt diese Art von Design Vorteile, die ich nicht sehe?

Dieser Ausschnitt Ihrer Frage ist hier der wichtigste Teil Ihrer Entwurfsentscheidung, und die Antwort ist (wie immer) davon abhängig .

Wenn Sie die Bibliothek nur über COM Interop verwenden möchten, sollten Sie das gesamte System unter diesem Gesichtspunkt entwerfen. Es gibt keinen Grund, die Zeilenanzahl zu verdreifachen. Je mehr LoC im System vorhanden ist, desto mehr Fehler treten auf. Sie sollten Ihr System daher so gestalten, dass nur COM-kompatible Funktionen verwendet werden.

Allerdings kann, glaube ich nicht aus einem guten Grunddie Verwendung einer NetBibliothek zu COM zu beschränken. Warum möchten Sie die Assembly nicht in einem anderen .NET-Code verwenden können? Es macht wenig Sinn, sich auf diese Weise einzuschränken.

Ich habe einige Erfahrungen damit, daher werde ich mitteilen, wie ich damit umgegangen bin. Es ist sicherlich nicht ideal. Ich habe einige Kompromisse gemacht. Ich verwende nur eine Fassade für COM-Klassen (wirklich Schnittstellen), die nicht mit den neueren .Net-Redewendungen kompatibel sind. Andernfalls setze ich die .Net-Schnittstelle direkt COM aus. Dies bedeutet im Grunde, dass ich eine Fassade für jede Schnittstelle verwende, die Generika verwendet. Generika sind das einzige, was mir begegnet ist, das einfach nicht kompatibel ist. Leider bedeutet dies eine Fassade für alles, was eine zurückgibt IEnumerable<T>, was ziemlich häufig ist.

Dies ist jedoch bei weitem nicht so schlimm, wie es scheint, da Ihre Fassadenklasse von Ihrer .Net-Klasse erbt und eine bestimmte Interop-Schnittstelle implementiert. Etwas wie das.

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
    }
}

Dies reduziert Ihren doppelten Code auf ein absolutes Minimum und schützt Ihre COM-Schnittstelle vor "unbeabsichtigten" Änderungen. Wenn sich Ihre Kernklasse / Schnittstelle ändert, muss Ihre Adapterklasse mehr Methoden außer Kraft setzen (oder die Schnittstelle beschädigen und die Hauptversionsnummer erhöhen). Andererseits wird nicht der gesamte Interop-Code im InteropNamespace gespeichert , was zu einer verwirrenden Dateiorganisation führen kann. Wie ich schon sagte, es ist ein Kompromiss.

Ein ausführliches Beispiel hierfür finden Sie in den Ordnern SourceControl und Interop meines Repos. ISourceControlProviderund GitProviderwird von besonderem Interesse sein.

Badeente
quelle
Danke für die Antwort @RubberDuck! Ich bin vor ein paar Tagen auf das RubberDuck-Projekt gestoßen. Es war sehr interessant, den Code durchzulesen. Können Sie IEnumerablean VBA übergeben? Außerdem haben Sie im Rubberduck.Interop.GitProviderWarum parametrisierte Konstruktoren definiert, wenn COM sie nicht unterstützt?
Robodude666
Ja, Sie können eine IEnumerableCOM übergeben, es muss sich jedoch um die ältere, nicht generische Version handeln. Schauen Sie sich die Konstruktoren an SourceControlClassFactory. Grundsätzlich fälschen Sie es, indem Sie eine Instanz einer Klasse initialisieren, für deren ctor keine Parameter erforderlich sind, und sie verwenden, um Zugriff auf neue Instanzen der Klassen Ihrer API zu erhalten.
RubberDuck
Ich vermute, dass alles, was in Ihren ComVisible(true)Klassen / Schnittstellen definiert ist und von COM nicht unterstützt wird, einfach "ignoriert" wird. Ich vermute auch, wenn Sie eine Klasse mit demselben Namen in mehreren Namespaces definiert haben, müssten Sie eine eindeutige angeben, ProgIdwenn Sie mehr als eine davon verfügbar machen möchten.
Robodude666
Ja. Das ist richtig und staticMethoden werden ignoriert. Ich versuche zu sehen, ob es ein Äquivalent zu VB6 VB_PredeclaredIdgibt. Ich denke, es könnte möglich sein, eine Standardinstanz zu erstellen.
RubberDuck
7

es nimmt von der Schönheit ihrer .NET-Bibliothek weg

Dieser Autor braucht einen Weckschlag ins Gesicht. Für jedes professionelle Projekt besteht das Ziel darin, funktionierende Software zu entwickeln, die die Benutzer verwenden möchten. Jede Art von fehlgeleiteten Idealen über Schönheit kommt lange danach. Wenn dies ihre einzige Begründung ist, hat der Autor jegliche Glaubwürdigkeit verloren.

Es ist enorm nützlich, Ihre Klassen als sichtbar zu markieren und über COM mit Ihrem .NET-Code zu kommunizieren. Es ist verrückt, diesen großen Nutzen für die Produktivität der Schönheit zu verwerfen.

Damit die Dinge mit COM funktionieren, sind jedoch einige Kompromisse erforderlich. Ein Konstruktor ohne Argumente ist einer davon, und einige der anderen Dinge, die Sie erwähnt haben. Wenn dies Funktionen sind, die Sie sowieso nicht verwenden, oder wenn es Ihnen nicht schadet, sie nicht zu verwenden, müssen Sie viel Arbeit sparen, indem Sie dieselbe Klasse für COM und Nicht-COM verwenden.

Wenn Ihre COM-Clients andererseits eine dramatisch andere Benutzeroberfläche erwarten, Abhängigkeiten oder andere Einschränkungen aufweisen, die in Ihren .NET-Klassen unangenehm sind, oder wenn Sie erwarten, dass sich Ihre .NET-Klassen erheblich ändern und COM rückwärts beibehalten müssen, Kompatibilität, dann erstellen Sie auf jeden Fall eine Interop-Klasse, um diese Lücke zu schließen.

Aber denken Sie nicht an "Schönheit". Wenn Sie COM über .NET verfügbar machen können, sparen Sie Arbeit. Schaffe dir nicht mehr Arbeit.

Whatsisname
quelle
+1. Während ich die Schönheit der schwarzen Knöpfe auf einer Blackbox liebe, sind praktische Aspekte wesentlich wichtiger.
Gbjbaanb
Tatsächlich könnte die Person, die den Begriff "Schönheit" in diese Diskussion einführte, einen Weckschlag benötigen, und das war nicht der Autor dieses anderen Artikels.
Doc Brown
2
Nur eine Randnotiz: Wenn Ihre aktuellen Konstruktoren Argumente benötigen, ist es am besten, eine Klassenfactory für Ihre COM-Komponenten bereitzustellen, anstatt die vorhandene Schnittstelle / API neu zu gestalten.
RubberDuck
1
Ist es möglich, Ihre Fabrik als "GlobalMultiUse" -Klasse verfügbar zu machen? Sie können also einfach darauf Set oObject = MyCOMInterop.NewMyClass()verzichten, zuerst ein neues Factory-Objekt instanziieren zu müssen?
Robodude666
Das ist eine verdammt gute Frage @ robodude666. Nun, da Sie es erwähnen, hoffe ich es.
RubberDuck
3

Um fair zu sein, hat der ursprüngliche Autor dieses Artikels das Wort "Schönheit" nirgends in seinem Artikel verwendet, das waren Sie, was für diejenigen, die sich nicht die Zeit genommen haben, den Artikel zu lesen, einen voreingenommenen Eindruck hinterlassen könnte sich.

Soweit ich diesen Artikel verstanden habe, existierte die .NET-Bibliothek bereits und wurde ohne Berücksichtigung von COM geschrieben - wahrscheinlich, weil zum Zeitpunkt der Erstellung der Bibliothek keine Notwendigkeit bestand, COM zu unterstützen.

Später wurde die zusätzliche Anforderung zur Unterstützung von COM eingeführt. In einer solchen Situation haben Sie zwei Möglichkeiten:

  • Neugestaltung der ursprünglichen Bibliothek, um sie mit COM Interop kompatibel zu machen (mit dem Risiko, dass Dinge kaputt gehen)

  • Lassen Sie die ursprüngliche Arbeitsbibliothek größtenteils so wie sie ist, und erstellen Sie stattdessen eine separate Assembly mit der Funktion eines "Wrappers" (oder "Adapters").

Das Erstellen von Wrappern oder Adaptern ist ein bekanntes Entwurfsmuster (oder in diesem Fall eher ein Architekturmuster), und die Vor- und Nachteile sind ebenfalls bekannt. Ein Argument dafür ist die bessere "Trennung von Bedenken", und das hat nichts mit Schönheit zu tun.

Zu Ihren Anliegen

Jetzt müssen Sie Ihre .NET-Bibliothek UND eine Interop-Bibliothek einem Komponententest unterziehen.

Wenn Sie Ihre COM-Schnittstelle durch Neugestaltung in Ihre vorhandene .NET-Bibliothek einführen, müssen Sie diese Schnittstelle ebenfalls testen. Für die Einführung eines manuell geschriebenen Adapters sind möglicherweise einige zusätzliche Tests erforderlich, damit die Schnittstelle funktioniert. Es ist jedoch nicht erforderlich, alle Komponententests der Kerngeschäftsfunktionalität der Bibliothek zu kopieren. Vergessen Sie nicht, dass es nur eine weitere Schnittstelle zur Bibliothek ist.

Jetzt müssen Sie Zeit damit verbringen, herauszufinden, wie Sie Ihre schöne .NET-Bibliothek umgehen und sie COM zugänglich machen können.

Wenn Sie die Originalbibliothek neu gestalten müssen, müssen Sie das auch herausfinden, und ich denke, es wird viel mehr Arbeit sein.

Sie müssen Ihre Klassenanzahl effektiv verdoppeln oder verdreifachen.

Nur für die Klassen, die über die öffentliche COM-Schnittstelle verfügbar gemacht werden. Dies sollte eine kleine Anzahl der Klassen sein, die Teil der ursprünglichen Bibliothek sind.

Für eine andere Situation, die Sie erwähnt haben, wenn Sie bereits von Anfang an wissen, dass Sie COM als erstklassigen Bürger unterstützen müssen, sind Sie meiner Meinung nach richtig: Man sollte sich den Aufwand ersparen, eine separate Adapterbibliothek hinzuzufügen und die zu entwerfen .NET lib mit COM-Unterstützung direkt.

Doc Brown
quelle
(+1) "... was eine kleine Anzahl von ... der ursprünglichen Bibliothek sein sollte" ist jedoch nur manchmal wahr. Es gibt viele Arten von Projekten, die im "Klassenbibliotheksstil" entworfen wurden, wobei eine Klasse einem öffentlich sichtbaren Zweck entspricht und diese Klassen zusammengesetzt werden, um interessante Funktionen hervorzubringen. In diesem Fall kann es zu einer Eins-zu-Eins-Zuordnung zwischen der Anzahl der .NET-Klassen und der Anzahl der COM-Interop-Klassen kommen.
Rwong