Melhor escrever sua biblioteca .NET com limitações de COM em mente ou separar sua biblioteca .NET da Interop?

9

Me deparei com este artigo interessante: Como eu vim a amar a interoperabilidade COM no CodeProject, o que me fez pensar ...

O autor argumenta que eles não desejam nenhuma COM em sua biblioteca .NET porque isso tira a beleza da biblioteca .NET. Em vez disso, eles preferem escrever uma biblioteca de interoperabilidade separada que expõe sua biblioteca .NET ao COM. Essa biblioteca de interoperabilidade lidaria com o fato de o COM não suportar construtores com parâmetros, métodos sobrecarregados, genéricos, herança, métodos estáticos etc.

E embora eu ache isso muito inovador, ele não apenas complementa o projeto?

  • Agora você precisa testar a sua biblioteca .NET e uma biblioteca de interoperabilidade.
  • Agora você precisa dedicar um tempo para descobrir como solucionar sua bela biblioteca .NET e expô-la ao COM.
  • Você precisa, efetivamente, dobrar ou triplicar sua contagem de turmas.

Certamente entendo se você precisa da sua biblioteca para suportar COM e não COM. No entanto, se você pretende apenas usar o COM, esse tipo de design traz benefícios que não vejo? Você ganha apenas os benefícios da linguagem C #?

Ou facilita o controle de versão da sua biblioteca, fornecendo um invólucro? Isso faz com que seus testes de unidade sejam executados mais rapidamente, não exigindo o uso de COM?

robodude666
fonte
2
Você quer dizer, além de poder usar ferramentas úteis para tornar o código real melhor / mais limpo? E fornecendo um caminho de migração claro para esse material COM (presumivelmente herdado)?
Telastyn 24/03
3
Isso é como um padrão de fachada escrito em um espaço de nome inteiro. Eu acho que é inteligente organizá-lo assim, se você deseja usar os recursos do .NET, mas realmente precisa da automação OLE. Seus wrappers COM serão basicamente converter idiomas modernos (imutáveis? Genéricos?) Em objetos COM mais antigos, com construtores sem parâmetros, objetos altamente mutáveis ​​e SAFEARRAYs para suas listas genéricas. Se você estiver suportando outros componentes .NET ou estiver totalmente apaixonado pelos novos estilos, isso faria sentido. Se você APENAS suporta o COM, por que não escrever tudo no VB6 (32 bits) e manter apenas um idioma?
Mike Suporta Monica
3
@MasonWheeler: Ele foi preterido da mesma maneira que todo software com mais de 6 meses é "legado", estou certo?
Whatsisname
2
O @MasonWheeler COM não pode ser depreciado, não importa quantas pessoas (incluindo alguns grupos da Microsoft) desejariam, porque ainda é a melhor opção para controlar as coisas fora de processo.
Mike Suporta Monica
2
@ Mike, na verdade, estou olhando para portar um projeto VB6 para C # .NET. As APIs voltadas para o usuário que usamos são muito simples; no entanto, o código nos bastidores não é sustentável como está. Espero implementar a maioria da biblioteca em C # .NET e fornecer uma Interop Facade que forneça as APIs simples que interagem com as várias classes.
robodude666

Respostas:

7

No entanto, se você pretende apenas usar o COM, esse tipo de design traz benefícios que não vejo?

Esse trecho da sua pergunta é a parte mais importante da sua decisão de design aqui, e a resposta é (como sempre) depende .

Se você pretende apenas usar a biblioteca via COM Interop, deve projetar o sistema inteiro com isso em mente. Não há motivo para triplicar a contagem de linhas. Quanto mais LoC no sistema, mais defeitos ele terá. Portanto, você deve projetar seu sistema para usar apenas a funcionalidade compatível com.

No entanto , não consigo pensar em um bom motivo para restringir o uso de uma biblioteca .Net ao COM. Por que você não gostaria de poder usar o assembly em outro código .Net? Não faz muito sentido limitar-se dessa maneira.

Eu tenho alguma experiência com isso, então vou compartilhar como eu lidei com isso. Certamente não é o ideal. Eu fiz algumas compensações. Eu só uso uma fachada para classes COM (interfaces realmente) incompatíveis com os idiomas .Net mais recentes; caso contrário, eu exponho diretamente a interface .Net ao COM. Isso basicamente significa que eu uso uma fachada para qualquer interface que use genéricos. Os genéricos são a única coisa que encontrei que são simplesmente incompatíveis. Infelizmente, isso significa uma fachada para qualquer coisa que retorne um IEnumerable<T>, o que é bastante comum.

No entanto, isso não é tão ruim quanto parece, porque sua classe de fachada herda de sua classe .Net e implementa uma interface de interoperabilidade específica. Algo assim.

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

Isso mantém seu código duplicado no mínimo absoluto e protege sua interface COM contra alterações "não intencionais". À medida que sua classe principal / interface muda, sua classe de adaptador precisa substituir mais métodos (ou interromper a interface e aumentar o número da versão principal). Por outro lado, nem todo o seu código de interoperabilidade é armazenado no Interopespaço para nome, o que pode levar a uma organização de arquivos confusa. Como eu disse, é uma troca.

Para um exemplo completo disso, você pode navegar pelas pastas SourceControl e Interop do meu repositório. ISourceControlProvidere GitProviderserá de particular interesse.

Pato de borracha
fonte
Obrigado pela resposta @RubberDuck! Encontrei o projeto RubberDuck alguns dias atrás; tem sido uma leitura muito interessante do código. Você pode passar IEnumerablepara o VBA? Além disso, no Rubberduck.Interop.GitProviderpor que você definiu construtores parametrizados se o COM não os suporta?
precisa saber é o seguinte
Sim, você pode passar IEnumerableatravés de COM, mas deve ser a versão mais antiga e não genérica. Quanto aos construtores, dê uma olhada no SourceControlClassFactory. Basicamente, você o finge, inicializando uma instância de uma classe que não precisa de parâmetros para seu ctor e usando-o para obter acesso a novas instâncias das classes da sua API.
precisa
Estou supondo que qualquer coisa definida nas suas ComVisible(true)classes / interfaces que o COM não suporta seja simplesmente "ignorada"? Também estou supondo que, se você tem uma classe com o mesmo nome definida em vários espaços para nome, você teria que fornecer um único ProgIdse quiser expor mais de um deles?
precisa saber é o seguinte
Sim. É isso mesmo e os staticmétodos são ignorados. Estou tentando ver se há um equivalente do VB6 VB_PredeclaredIddisponível. Eu estou pensando que pode ser possível criar uma instância padrão.
precisa
7

tira a beleza de sua biblioteca .NET

Esse autor precisa de um tapa na cara. Para qualquer projeto profissional, o objetivo é criar software de trabalho que as pessoas desejam usar. Qualquer tipo de ideal equivocado sobre beleza vem muito depois disso. Se esse é seu único raciocínio, o autor perdeu toda a credibilidade.

Ser capaz de marcar suas classes como visíveis em com e conversar com seu código .NET através do COM é extremamente útil. Descartar esse grande benefício da produtividade para a beleza é insano.

No entanto, fazer as coisas funcionarem com o COM requer algumas compensações, exigir um construtor sem argumento é um deles e algumas das outras coisas que você mencionou. Se esses são os recursos que você não está usando de qualquer maneira, ou não vai prejudicar você não usá-los, há muito trabalho a ser salvo usando a mesma classe para COM e não COM.

Por outro lado, se seus clientes COM esperam uma interface dramaticamente diferente, possuam dependências ou outras limitações desagradáveis ​​para lidar em suas classes .NET, ou você espera alterar significativamente suas classes .NET e precisa manter a compatibilidade com versões anteriores. compatibilidade, crie, por todos os meios, uma classe Interop para preencher essa lacuna.

Mas não pense em "beleza". Ser capaz de expor o COM através do .NET visa poupar seu trabalho. Não crie mais trabalho para si mesmo.

whatsisname
fonte
+1. Enquanto eu amo a beleza dos botões pretos em uma caixa preta, os aspectos práticos são significativamente mais importantes.
precisa
Na verdade, a pessoa que introduziu o termo "beleza" nessa discussão pode precisar de um tapa de alerta, e esse não foi o autor desse outro artigo.
Doc Brown
2
Apenas uma observação, se seus construtores atuais exigirem argumentos, é melhor fornecer uma fábrica de classes para seus componentes COM, em vez de redesenhar a interface / API existente.
RubberDuck
11
É possível expor sua fábrica como uma classe "GlobalMultiUse"? Então você pode fazer isso Set oObject = MyCOMInterop.NewMyClass()sem precisar instanciar um novo objeto Factory?
precisa saber é o seguinte
Essa é uma pergunta muito boa @ robodude666. Agora que você mencionou, espero que sim.
precisa
3

Bem, para ser sincero, o autor original desse artigo não usou a palavra "beleza" em nenhum lugar do artigo, que era você, o que poderia dar uma impressão tendenciosa para aqueles que não tiveram tempo de ler o artigo por si mesmos.

Até onde eu entendi esse artigo, a biblioteca .NET já existia e foi escrita sem o COM - provavelmente porque no momento em que a biblioteca foi criada, não havia nenhum requisito para oferecer suporte ao COM.

Posteriormente, foi introduzido o requisito adicional de suporte à COM. Diante de tal situação, você tem duas opções:

  • redesenhar a lib original para torná-la compatível com a interoperabilidade COM (com o risco de quebrar as coisas)

  • deixe a biblioteca de trabalho original principalmente como está e, em vez disso, crie um assembly separado com a função de um "wrapper" (ou "adaptador")

Criar wrappers ou adaptadores é um padrão de design bem conhecido (ou, nesse caso, mais um padrão de arquitetura), e os prós e contras também são bem conhecidos. Um argumento para isso é a melhor "separação de preocupações", e isso não tem nada a ver com beleza.

Para suas preocupações

Agora você precisa testar a sua biblioteca .NET e uma biblioteca de interoperabilidade.

Quando você apresenta sua interface COM à sua biblioteca .NET existente, redesenhando, você também deve testar essa interface. A introdução de um adaptador gravado manualmente pode precisar de alguns testes adicionais em que a interface está funcionando, é claro, mas não há necessidade de copiar todos os testes de unidade da funcionalidade de negócios principal da lib. Não esqueça que é apenas mais uma interface para a lib.

Agora você precisa dedicar um tempo para descobrir como solucionar sua bela biblioteca .NET e expô-la ao COM.

Quando você precisa reprojetar a biblioteca original, também precisa descobrir isso, e acho que será muito mais trabalhoso.

Você precisa, efetivamente, dobrar ou triplicar sua contagem de turmas.

Somente para as classes expostas pela interface pública do COM, que deve ser um pequeno número de classes que fazem parte da biblioteca original.

Disse que, para uma situação diferente que você mencionou, quando você já sabe que precisa apoiar o COM como cidadão de primeira classe desde o início, acho que está correto: você deve evitar o incômodo de adicionar uma lib de adaptador separada e projetar o .NET lib com suporte COM diretamente.

Doc Brown
fonte
(+1) No entanto, "... que deve ser um pequeno número de ... a biblioteca original" às vezes é verdade. Existem muitos tipos de projetos criados no "estilo de biblioteca de classes", em que uma classe é igual a uma finalidade publicamente visível, e essas classes são compostas para gerar uma funcionalidade interessante. Nesse caso, pode haver um mapeamento individual entre a contagem de classes .NET e a contagem de classes de interoperabilidade COM.
Rwong