Meglio scrivere la tua libreria .NET tenendo conto delle limitazioni COM o separare la tua libreria .NET da Interop?


9

Mi sono imbattuto in questo interessante articolo: How I Came to Love COM Interoperability su CodeProject, che mi ha fatto pensare ...

L'autore sostiene che non vogliono alcun COM-ities nella loro libreria .NET perché toglie la bellezza della loro libreria .NET. Invece, preferirebbero scrivere una libreria Interop separata che esponga la loro libreria .NET a COM. Questa libreria Interop gestirà il fatto che COM non supporta i costruttori con parametri, metodi sovraccaricati, generici, ereditarietà, metodi statici, ecc.

E mentre penso che sia molto nuovo, non si adatta solo al progetto?

  • Ora devi testare l'unità della tua libreria .NET E di una libreria Interop.
  • Ora devi dedicare tempo a capire come aggirare la tua bellissima libreria .NET ed esporla a COM.
  • È necessario, effettivamente, raddoppiare o triplicare il conteggio delle lezioni.

Posso certamente capire se hai bisogno della tua libreria per supportare sia COM che non COM. Tuttavia, se hai intenzione di utilizzare solo COM questo tipo di design porta qualche vantaggio che non vedo? Ottieni solo i vantaggi del linguaggio C #?

Oppure semplifica il controllo delle versioni della libreria fornendo un wrapper? Rende i test delle unità più veloci non richiedendo l'uso di COM?


2
Intendi, oltre a poter usare strumenti utili per rendere il codice reale migliore / più pulito? E fornire un chiaro percorso di migrazione per quella roba COM (presumibilmente legacy)?
Telastyn,

3
Questo è come uno schema di facciata scritto su un intero spazio dei nomi. Penso che sia intelligente organizzarlo in questo modo se si desidera utilizzare le funzionalità .NET ma è davvero necessaria l'automazione OLE. I wrapper COM saranno fondamentalmente per convertire moderni idiomi (immutabili? Generici?) In vecchi oggetti COM con costruttori senza parametri, oggetti fortemente mutabili e SAFEARRAY per i tuoi elenchi generici. Se stai supportando altri componenti .NET o sei totalmente innamorato dei nuovi stili, questo avrebbe senso. Se stai supportando SOLO COM, allora perché non scrivere tutto in VB6 (32 bit) e mantenere solo un idioma?
Mike,

3
@MasonWheeler: è obsoleto allo stesso modo in cui tutti i software di età superiore ai 6 mesi sono "legacy", vero?
whatsisname

2
La COM COM @MasonWheeler non può essere deprezzata, non importa quante persone (compresi alcuni gruppi all'interno di Microsoft) vorrebbero, perché è ancora la scelta migliore per controllare le cose fuori processo.
Mike,

2
@ Mike, in realtà sto guardando il porting di un progetto VB6 su C # .NET. Le API rivolte all'utente che utilizziamo sono molto semplici, tuttavia il codice dietro le quinte non è mantenibile così com'è. Spero di implementare la maggior parte della libreria in C # .NET e di fornire una Interop Facade che fornisca le semplici API che interagiscono con le varie classi.
robodude666,

Risposte:


7

Tuttavia, se hai intenzione di utilizzare solo COM questo tipo di design porta qualche vantaggio che non vedo?

Questo frammento della tua domanda è la parte più importante della tua decisione di progettazione qui, e la risposta è (come sempre) dipende .

Se si intende utilizzare la libreria solo tramite interoperabilità COM, è necessario progettare l'intero sistema tenendo presente questo aspetto. Non c'è motivo di triplicare il conteggio delle righe. Più LoC nel sistema, più difetti avrà. Pertanto, è necessario progettare il sistema in modo che utilizzi solo funzionalità compatibili COM.

Tuttavia , non riesco a pensare a una buona ragione per limitare l'uso di una libreria .Net a COM. Perché non vorresti poter utilizzare l'assembly in un altro codice .Net? Ha poco senso limitarsi in questo modo.

Ho una certa esperienza con questo, quindi condividerò come l'ho gestito. Non è certamente l'ideale. Ho fatto alcuni compromessi. Uso solo una facciata per le classi COM (interfacce davvero) che sono incompatibili con i nuovi idiomi .Net, altrimenti espongo direttamente l'interfaccia .Net a COM. Questo in pratica significa che uso una facciata per qualsiasi interfaccia che usa generici. I generici sono l'unica cosa che ho incontrato che sono semplicemente incompatibili. Sfortunatamente, questo significa una facciata per tutto ciò che restituisce un IEnumerable<T>, che è abbastanza comune.

Tuttavia, questo non è così grave come sembra, perché la classe della facciata eredita dalla classe .Net e implementa un'interfaccia Interop specifica. Qualcosa come questo.

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

Ciò mantiene il codice duplicato al minimo assoluto e protegge l'interfaccia COM da modifiche "non intenzionali". Man mano che la classe principale / l'interfaccia cambia, la classe dell'adattatore deve superare altri metodi (o interrompere l'interfaccia e superare il numero di versione principale). D'altra parte, non tutto il codice di interoperabilità è archiviato nello Interopspazio dei nomi, il che può portare a un'organizzazione confusa dei file. Come ho detto, è un compromesso.

Per un esempio completo di ciò, è possibile sfogliare le cartelle SourceControl e Interop del mio repository. ISourceControlProvidere GitProvidersarà di particolare interesse.


Grazie per la risposta @RubberDuck! Ho incontrato il progetto RubberDuck qualche giorno fa; è stato molto interessante leggere il codice. Sei in grado di passare IEnumerablea VBA? Inoltre, Rubberduck.Interop.GitProviderperché hai definito costruttori parametrizzati se COM non li supporta?
robodude666,

Sì, è possibile passare un IEnumerableCOM COM, ma deve essere la versione precedente e non generica. Per quanto riguarda i costruttori, dai un'occhiata a SourceControlClassFactory. Fondamentalmente, lo falsi inizializzando un'istanza di una classe che non ha bisogno di parametri per il suo ctor e utilizzandola per ottenere l'accesso a nuove istanze delle classi della tua API.
RubberDuck,

Immagino che qualcosa di definito nelle tue ComVisible(true)classi / interfacce che COM non supporta sia semplicemente "ignorato"? Suppongo anche che se hai una classe con lo stesso nome definita in più spazi dei nomi, dovresti fornire un unico ProgIdse vuoi esporre più di uno di essi?
robodude666,

Sì. Esatto e i staticmetodi vengono ignorati. Sto cercando di vedere se è disponibile un equivalente di VB6 VB_PredeclaredId. Sto pensando che potrebbe essere possibile creare un'istanza predefinita.
RubberDuck,

7

toglie dalla bellezza della loro libreria .NET

Quell'autore ha bisogno di uno schiaffo in faccia. Per qualsiasi progetto professionale, l'obiettivo è creare software funzionanti che le persone desiderano utilizzare. Qualsiasi tipo di ideali sbagliati sulla bellezza viene molto dopo. Se questo è il loro unico ragionamento, l'autore ha perso tutta la credibilità.

Essere in grado di contrassegnare le tue classi come visibili e poter parlare con il tuo codice .NET tramite COM è estremamente utile. Scartare quel grande vantaggio per la produttività per la bellezza è folle.

Tuttavia, far funzionare le cose con COM richiede alcuni compromessi, richiedere un costruttore no-arg è uno di questi e alcune delle altre cose che hai menzionato. Se quelle sono funzionalità che non stai usando comunque, o non ti farà male non usarle, allora c'è molto lavoro da salvare usando la stessa classe per COM e non com.

D'altra parte, se i tuoi client COM si aspettano un'interfaccia radicalmente diversa, hanno dipendenze o altre limitazioni che sono brutte da gestire nelle tue classi .NET, o ti aspetti di cambiare significativamente le tue classi .NET e devi mantenere COM indietro compatibilità, quindi creare una classe Interop per colmare tale divario.

Ma non pensare alla "bellezza". Essere in grado di esporre COM tramite .NET significa risparmiare lavoro. Non creare più lavoro per te stesso.


+1. Mentre adoro la bellezza dei pulsanti neri su una scatola nera, le funzionalità sono significativamente più importanti.
gbjbaanb,

In realtà, la persona che ha introdotto il termine "bellezza" in questa discussione potrebbe aver bisogno di uno schiaffo e che non era l'autore di quell'altro articolo.
Doc Brown,

2
Solo una nota a margine, se i tuoi attuali costruttori richiedono arg, è meglio fornire una factory di classe per i tuoi componenti COM, piuttosto che ridisegnare l'interfaccia / API esistente.
RubberDuck,

1
È possibile esporre la propria fabbrica come classe "GlobalMultiUse"? Quindi puoi semplicemente fare a Set oObject = MyCOMInterop.NewMyClass()meno di dover prima istanziare un nuovo oggetto Factory?
robodude666,

Questa è una domanda dannatamente buona @ robodude666. Ora che me lo dici, lo spero .
RubberDuck,

3

Beh, per essere onesti, l'autore originale di quell'articolo non ha usato la parola "bellezza" in nessun punto del suo articolo, sei stato tu, che potresti dare un'impressione parziale su ciò per coloro che non hanno avuto il tempo di leggere l'articolo loro stessi.

Per quanto ho capito quell'articolo, la libreria .NET esisteva già ed è stata scritta senza COM in mente - probabilmente perché al momento della creazione della libreria non era necessario supportare COM.

Successivamente, è stato introdotto il requisito aggiuntivo di supportare COM. Di fronte a una situazione del genere, hai due opzioni:

  • riprogettare la lib originale per renderla compatibile con l'interoperabilità COM (con il rischio di rompere le cose)

  • lasciare la libreria di lavoro originale per lo più così com'è e creare invece un assembly separato con la funzione di "wrapper" (o "adattatore")

La creazione di wrapper o adattatori è un modello di progettazione ben noto (o in questo caso più un modello architettonico), e anche i pro e i contro sono ben noti. Un argomento è la migliore "separazione delle preoccupazioni", che non ha nulla a che fare con la bellezza.

Alle tue preoccupazioni

Ora devi testare l'unità della tua libreria .NET E di una libreria Interop.

Quando si introduce l'interfaccia COM nella libreria .NET esistente mediante la riprogettazione, è necessario testare anche tale interfaccia. L'introduzione di un adattatore scritto manualmente potrebbe richiedere alcuni test aggiuntivi per il funzionamento dell'interfaccia, ovviamente, ma non è necessario copiare tutti i test unitari della funzionalità aziendale principale della lib. Non dimenticare che è solo un'altra interfaccia per la libreria.

Ora devi dedicare tempo a capire come aggirare la tua bellissima libreria .NET ed esporla a COM.

Quando devi ridisegnare la libreria originale, devi capirlo anche tu, e immagino che sarà molto più lavoro.

È necessario, effettivamente, raddoppiare o triplicare il conteggio delle lezioni.

Solo per le classi che sono esposte attraverso l'interfaccia COM pubblica, che dovrebbe essere un piccolo numero delle classi parte della libreria originale.

Detto questo, per una situazione diversa che hai menzionato, quando sai già che devi supportare COM come cittadino di prima classe fin dall'inizio, penso che tu abbia ragione: si dovrebbe salvare la seccatura di aggiungere un lib adattatore separato e progettare il Lib. NET con supporto COM direttamente.


(+1) Tuttavia, "... che dovrebbe essere un piccolo numero di ... la libreria originale" è solo a volte vero. Esistono molti tipi di progetti progettati nello "stile di libreria di classe", in cui una classe equivale a uno scopo pubblicamente visibile e che le classi compongono per dare origine a funzionalità interessanti. In tal caso, potrebbe esserci un mapping uno a uno tra il conteggio delle classi .NET e il conteggio delle classi di interoperabilità COM.
rwong,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.