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?
fonte
Respostas:
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.
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
Interop
espaç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.
ISourceControlProvider
eGitProvider
será de particular interesse.fonte
IEnumerable
para o VBA? Além disso, noRubberduck.Interop.GitProvider
por que você definiu construtores parametrizados se o COM não os suporta?IEnumerable
através de COM, mas deve ser a versão mais antiga e não genérica. Quanto aos construtores, dê uma olhada noSourceControlClassFactory
. 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.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 únicoProgId
se quiser expor mais de um deles?static
métodos são ignorados. Estou tentando ver se há um equivalente do VB6VB_PredeclaredId
disponível. Eu estou pensando que pode ser possível criar uma instância padrão.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.
fonte
Set oObject = MyCOMInterop.NewMyClass()
sem precisar instanciar um novo objeto Factory?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
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.
Quando você precisa reprojetar a biblioteca original, também precisa descobrir isso, e acho que será muito mais trabalhoso.
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.
fonte