¿Es mejor escribir su biblioteca .NET teniendo en cuenta las limitaciones de COM, o separar su biblioteca .NET de Interop?

9

Me encontré con este interesante artículo: Cómo llegué a amar la interoperabilidad COM en CodeProject, que me hizo pensar ...

El autor argumenta que no quieren COM-ities en su biblioteca .NET porque les quita la belleza de su biblioteca .NET. En cambio, prefieren escribir una biblioteca Interop separada que exponga su biblioteca .NET a COM. Esta biblioteca de interoperabilidad manejaría el hecho de que COM no admite constructores con parámetros, métodos sobrecargados, genéricos, herencia, métodos estáticos, etc.

Y si bien creo que es muy novedoso, ¿no solo completa el proyecto?

  • Ahora necesita probar su biblioteca .NET Y una biblioteca Interop.
  • Ahora debe dedicar tiempo a descubrir cómo solucionar su hermosa biblioteca .NET y exponerla a COM.
  • Necesita, efectivamente, duplicar o triplicar el recuento de clases.

Ciertamente puedo entender si necesita que su biblioteca sea compatible con COM y no COM. Sin embargo, si solo tiene la intención de usar COM, ¿este tipo de diseño trae algún beneficio que no veo? ¿Solo obtiene los beneficios del lenguaje C #?

¿O facilita el control de versiones de su biblioteca al proporcionarle un contenedor? ¿Hace que sus pruebas unitarias se ejecuten más rápido al no requerir el uso de COM?

robodude666
fuente
2
¿Quiere decir, además de poder utilizar herramientas útiles para mejorar / limpiar el código real? ¿Y proporcionar una ruta de migración clara para ese material COM (presumiblemente heredado)?
Telastyn
3
Esto es como un patrón de fachada escrito a lo largo de un espacio de nombres completo. Creo que es inteligente organizarlo de esta manera si desea utilizar las funciones .NET pero realmente necesita la automatización OLE. Sus envoltorios COM básicamente serán para convertir modismos modernos (¿inmutables? ¿Genéricos?) En objetos COM más antiguos con constructores sin parámetros, objetos muy mutables y SAFEARRAY para sus listas genéricas. Si tiene soporte para otros componentes .NET o está totalmente enamorado de los nuevos estilos, esto tendría sentido. Si SOLO está apoyando COM, entonces ¿por qué no escribir todo en VB6 (32 bits) y mantener solo un idioma?
Mike
3
@MasonWheeler: Está en desuso de la misma manera que todo el software de más de 6 meses es "heredado", ¿estoy en lo cierto?
whatsisname
2
@MasonWheeler COM no se puede depreciar sin importar cuánto deseen las personas (incluidos algunos grupos dentro de Microsoft) porque sigue siendo la mejor opción para controlar las cosas fuera de proceso.
Mike
2
@ Mike, en realidad estoy buscando portar un proyecto VB6 a C # .NET. Las API orientadas al usuario que utilizamos son muy simples, sin embargo, el código detrás de escena no se puede mantener tal cual. Espero implementar la mayoría de la biblioteca en C # .NET y proporcionar una Fachada de interoperabilidad que proporcione las API simples que interactúan con las diversas clases.
robodude666

Respuestas:

7

Sin embargo, si solo tiene la intención de usar COM, ¿este tipo de diseño trae algún beneficio que no veo?

Este fragmento de su pregunta es la parte más importante de su decisión de diseño aquí, y la respuesta es (como siempre) depende .

Si solo tiene la intención de utilizar la biblioteca a través de COM Interop, debe diseñar todo el sistema teniendo esto en cuenta. No hay razón para triplicar el recuento de líneas. Mientras más LoC haya en el sistema, más defectos tendrá. Por lo tanto, debe diseñar su sistema para usar solo la funcionalidad compatible con COM.

Sin embargo , no puedo pensar en una buena razón para restringir el uso de una biblioteca .Net a COM. ¿Por qué no querrías poder usar el ensamblado en otro código .Net? Tiene poco sentido limitarse de esta manera.

Tengo algo de experiencia con esto, así que compartiré cómo lo he manejado. Ciertamente no es lo ideal. He hecho algunas compensaciones. Solo uso una fachada para las clases COM (interfaces realmente) que son incompatibles con los modismos .Net más nuevos, de lo contrario, expongo directamente la interfaz .Net a COM. Esto básicamente significa que uso una fachada para cualquier interfaz que use genéricos. Los genéricos son lo único que he encontrado que son simplemente incompatibles. Desafortunadamente, esto significa una fachada para todo lo que devuelve un IEnumerable<T>, que es bastante común.

Sin embargo, esto no es tan malo como parece, porque su clase de fachada hereda de su clase .Net e implementa una interfaz Interop específica. Algo como esto.

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

Esto mantiene su código duplicado a un mínimo absoluto y protege su interfaz COM de cambios "no intencionales". A medida que su clase principal / interfaz cambia, su clase de adaptador tiene que anular más métodos (o romper la interfaz y cambiar el número de versión principal). Por otro lado, no todo su código de interoperabilidad se almacena en el Interopespacio de nombres, lo que puede llevar a una organización de archivos confusa. Como dije, es una compensación.

Para un ejemplo completo de esto, puede navegar a través de las carpetas SourceControl e Interop de mi repositorio. ISourceControlProvidery GitProviderserá de particular interés.

RubberDuck
fuente
Gracias por la respuesta @RubberDuck! Me encontré con el proyecto RubberDuck hace unos días; Ha sido muy interesante leer el código. ¿Eres capaz de pasar IEnumerablea VBA? Además, en Rubberduck.Interop.GitProvider¿por qué definió constructores parametrizados si COM no los admite?
robodude666
Sí, puede pasar un IEnumerableCOM a través, pero tiene que ser la versión anterior, no genérica. En cuanto a los constructores, eche un vistazo a SourceControlClassFactory. Básicamente, lo falsifica inicializando una instancia de una clase que no necesita parámetros para su ctor y usándola para obtener acceso a nuevas instancias de las clases de su API.
RubberDuck
¿Supongo que cualquier cosa definida en sus ComVisible(true)clases / interfaces que COM no admite simplemente se "ignora"? También supongo que si tiene una clase del mismo nombre definida en múltiples espacios de nombres, ¿tendría que proporcionar una única ProgIdsi desea exponer más de uno?
robodude666
Si. Eso es correcto y los staticmétodos son ignorados. Estoy tratando de ver si hay un equivalente de VB6 VB_PredeclaredIddisponible. Estoy pensando que podría ser posible crear una instancia predeterminada.
RubberDuck
7

les quita la belleza de su biblioteca .NET

Ese autor necesita una bofetada en la cara. Para cualquier proyecto profesional, el objetivo es hacer que el software funcione y que la gente quiera usar. Cualquier tipo de ideales equivocados sobre la belleza vienen mucho después de eso. Si ese es su único razonamiento, el autor ha perdido toda credibilidad.

Poder marcar sus clases como visibles y poder hablar con su código .NET a través de COM es enormemente útil. Descartar ese gran beneficio para la productividad de la belleza es una locura.

Sin embargo, hacer que las cosas funcionen con COM requiere algunas compensaciones, lo que requiere un constructor sin argumentos es una de ellas, y algunas de las otras cosas que mencionó. Si esas son características que no está utilizando de todos modos, o no le hará daño no usarlas, entonces hay mucho trabajo que guardar al usar la misma clase para COM y no com.

Por otro lado, si sus clientes COM esperan una interfaz dramáticamente diferente, tienen dependencias u otras limitaciones que son desagradables para tratar en sus clases .NET, o esperan cambiar significativamente sus clases .NET y necesitan mantener COM al revés. compatibilidad, entonces crea una clase de interoperabilidad para cerrar esa brecha.

Pero no pienses en la "belleza". Poder exponer COM a través de .NET está destinado a ahorrarle trabajo. No crees más trabajo para ti.

whatsisname
fuente
+1. Aunque me encanta la belleza de los botones negros en una caja negra, los aspectos prácticos son significativamente más importantes.
gbjbaanb
En realidad, la persona que introdujo el término "belleza" en esta discusión podría necesitar una bofetada de despertador, y ese no fue el autor de ese otro artículo.
Doc Brown
2
Solo una nota al margen, si sus constructores actuales requieren argumentos, es mejor proporcionar una fábrica de clase para sus componentes COM, en lugar de rediseñar la interfaz / API existente.
RubberDuck
1
¿Es posible exponer su fábrica como una clase "GlobalMultiUse"? ¿Entonces puede hacerlo Set oObject = MyCOMInterop.NewMyClass()sin tener que instanciar primero un nuevo objeto Factory?
robodude666
Esa es una muy buena pregunta @ robodude666. Ahora que lo mencionas, espero que sí.
RubberDuck
3

Bueno, para ser justos, el autor original de ese artículo no usó la palabra "belleza" en ninguna parte de su artículo, ese era usted, lo que podría dar una impresión parcial sobre eso para aquellos que no se tomaron el tiempo para leer el artículo. sí mismos.

Hasta donde he entendido ese artículo, la biblioteca .NET ya existía y se escribió sin COM en mente, probablemente porque en el momento en que se creó la biblioteca, no había ningún requisito para admitir COM.

Más tarde, se introdujo el requisito adicional de soportar COM. Ante tal situación, tiene dos opciones:

  • rediseñar la lib original para que sea compatible con la interoperabilidad COM (con el riesgo de romper cosas)

  • deje que la biblioteca de trabajo original esté como está, y cree un ensamblaje separado con la función de "envoltorio" (o "adaptador")

La creación de envoltorios o adaptadores es un patrón de diseño bien conocido (o en este caso más bien un patrón arquitectónico), y los pros y los contras también son bien conocidos. Un argumento a favor es la mejor "separación de preocupaciones", y eso no tiene nada que ver con la belleza.

A sus inquietudes

Ahora necesita probar su biblioteca .NET Y una biblioteca Interop.

Cuando presenta su interfaz COM a su biblioteca .NET existente mediante el rediseño, también debe probar esa interfaz. La introducción de un adaptador escrito manualmente puede necesitar algunas pruebas adicionales de que la interfaz está funcionando, por supuesto, pero no es necesario copiar todas las pruebas unitarias de la funcionalidad comercial central de la lib. No olvides que es solo otra interfaz para la lib.

Ahora debe dedicar tiempo a descubrir cómo solucionar su hermosa biblioteca .NET y exponerla a COM.

Cuando tienes que rediseñar la biblioteca original, también tienes que resolverlo, y supongo que será mucho más trabajo.

Necesita, efectivamente, duplicar o triplicar el recuento de clases.

Solo para las clases que están expuestas a través de la interfaz COM pública, que debería ser un pequeño número de las clases que forman parte de la biblioteca original.

Dijo que, para una situación diferente que mencionó, cuando ya sabe que tiene que apoyar a COM como ciudadano de primera clase desde el principio, creo que está en lo correcto: uno debe evitar la molestia de agregar un adaptador separado lib y diseñar el .NET lib con soporte COM directamente.

Doc Brown
fuente
(+1) Sin embargo, "... que debería ser un pequeño número de ... la biblioteca original" a veces solo es cierto. Hay muchos tipos de proyectos diseñados en el "estilo de biblioteca de clases", donde una clase es igual a un propósito visible públicamente, y esas clases se componen para dar lugar a una funcionalidad interesante. En ese caso, podría haber un mapeo uno a uno entre el recuento de clases .NET y el recuento de clases de interoperabilidad COM.
rwong