更好地编写带有COM限制的.NET库,还是将.NET库与Interop分开?


9

我碰到了一篇有趣的文章:我如何爱上 CodeProject上的COM互操作性,这让我开始思考...

作者认为,他们不希望在.NET库中有任何COM-ity,因为它剥夺了.NET库的美感。相反,他们宁愿编写一个单独的Interop库,以将其.NET库公开给COM。该Interop库将处理以下事实:COM不支持带参数,重载方法,泛型,继承,静态方法等的构造方法。

虽然我认为这很新颖,但它不只是使项目变得复杂吗?

  • 现在,您需要对.NET库和一个Interop库进行单元测试。
  • 现在,您需要花时间弄清楚如何解决漂亮的.NET库并将其公开给COM。
  • 您需要有效地将班级人数增加一倍或两倍。

我当然可以理解您是否需要您的库来支持COM和非COM。但是,如果仅打算使用COM,这种设计是否会带来我看不到的好处?您仅获得C#语言的好处吗?

还是通过提供包装器使库的版本控制变得容易?是否通过不需要使用COM来使您的单元测试运行更快?


2
您的意思是,除了能够使用有用的工具来使真实的代码更好/更清洁之外?为这些(大概是旧的)COM东西提供清晰的迁移路径吗?
Telastyn 2015年

3
这就像一个覆盖整个命名空间的外观模式。如果您想使用.NET功能,但确实需要OLE Automation,我认为将其组织为这样是很明智的。您的COM包装器基本上将使用没有参数的构造函数,大量易变的对象和SAFEARRAYs将现代(不可变?泛型?)成语转换为较旧的COM对象。如果您正在支持其他.NET组件,或者完全喜欢这些新样式,这将是有道理的。如果您仅支持COM,那为什么不将整个内容写在VB6(32位)中,并且只保留一个习惯用语呢?
Mike

3
@MasonWheeler:所有六个月以上的软件都以“旧版”的方式被弃用,对吗?
whatsisname 2015年

2
无论有多少人(包括Microsoft的某些小组)希望使用@MasonWheeler COM都不会贬值,因为它仍然是控制进程外内容的最佳选择。
Mike

2
@Mike,我实际上是在考虑将VB6项目移植到C#.NET。我们使用的面向用户的API非常简单,但是,幕后的代码无法按原样维护。我希望在C#.NET中实现大多数库,并提供一个Interop Facade,它提供与各种类进行交互的简单API。
robodude666

Answers:


7

但是,如果仅打算使用COM,这种设计是否会带来我看不到的好处?

你的问题的这个片段是你的设计决策的最重要的部分在这里,答案是(一如既往),它依赖

如果仅打算通过COM Interop使用该库,则应牢记这一点设计整个系统。没有理由使行数增加三倍。系统中的LoC越多,缺陷就会越多。因此,您应该将系统设计为仅使用COM兼容功能。

但是,我想不出将.Net库限制用于COM的充分理由。您为什么不希望能够在其他.Net代码中使用该程序集?这样限制自己毫无意义。

我对此有一些经验,因此我将分享我的处理方式。它当然不是理想的。我做了一些折衷。我只对与较新的.Net习惯用法不兼容的COM类(实际上是接口)使用外观,否则,我直接将.Net接口公开给COM。基本上,这意味着我会将外观用于任何使用泛型的接口。泛型是我遇到的唯一不兼容的东西。不幸的是,这意味着返回的任何事物的外观IEnumerable<T>,这是很常见的。

但是,这并不像看起来那样糟糕,因为您的Facade类继承自.Net类并实现了特定的Interop接口。这样的东西。

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

这样可以将重复的代码保持在最低限度,并保护您的COM接口免受“意外”更改。随着核心类/接口的更改,适配器类必须使用更多方法(或破坏接口并增加主版本号)。另一方面,并​​非所有的Interop代码都存储在Interop名称空间中,这可能导致文件组织混乱。就像我说的,这是一个权衡。

对于完整的示例,您可以浏览我的仓库的SourceControlInterop文件夹。ISourceControlProviderGitProvider会引起特别的兴趣。


感谢您的回复@RubberDuck!几天前,我遇到了RubberDuck项目。阅读代码非常有趣。您可以通过IEnumerableVBA吗?另外,Rubberduck.Interop.GitProvider为什么在COM不支持的情况下为什么要定义参数化构造函数呢?
robodude666

是的,您可以通过一个IEnumerableCOM,但是它必须是较旧的非通用版本。至于构造函数,请看一下SourceControlClassFactory。基本上,您通过初始化不需要其ctor参数的类的实例并使用它来访问API类的新实例来伪造它。
RubberDuck 2015年

我猜想您的ComVisible(true)类/接口中定义的任何COM不支持的内容都只是被“忽略”了?我还在猜测,如果您要在多个名称空间中定义同名的类,ProgId那么如果要公开多个名称空间,则必须提供一个唯一的名称?
robodude666

是。是的,static方法被忽略了。我正在尝试查看是否有等效的VB6 VB_PredeclaredId。我在想可能会创建一个默认实例。
RubberDuck

7

它带走了他们的.NET库的美丽

那位作者需要醒来一巴掌。对于任何专业项目,目标都是制作人们想要使用的工作软件。在那之后,关于美的任何一种误导的理想都会随之而来。如果这只是他们的唯一理由,那么作者将失去所有信誉。

能够将您的类标记为com-visible并能够通过COM与.NET代码进行对话非常有用。放弃对美容生产率的巨大好处是疯狂的。

但是,使事情与COM配合使用确实需要权衡取舍,其中一个需要no-arg构造函数,而您提到的其他一些东西也需要。如果这些功能还是您不使用,或者不使用它们不会对您造成伤害,那么对于COM和non com使用相同的类可以节省很多工作。

另一方面,如果您的COM客户端期望一个截然不同的接口,在.NET类中具有令人讨厌的依赖关系或其他限制,或者您希望显着更改.NET类并需要向后维护COM,兼容性,然后一定要创建一个Interop类来弥合这种差距。

但是不要考虑“美容”。能够通过.NET公开COM意味着可以节省您的工作。不要为自己创造更多的工作。


+1。虽然我喜欢黑匣子上黑色按钮之美,但实用性却更为重要。
gbjbaanb 2015年

实际上,在本次讨论中引入“美容”一词的人可能需要醒来一巴掌,而那不是其他文章的作者。
布朗

2
附带说明一下,如果您当前的构造函数需要args,则最好为COM组件提供一个类工厂,而不是重新设计现有的接口/ API。
RubberDuck

1
是否可以将工厂公开为“ GlobalMultiUse”类?因此,您Set oObject = MyCOMInterop.NewMyClass()不必先实例化新的Factory对象就可以执行操作吗?
robodude666

那是一个该死的好问题,@ robodude666。现在您提到了,我希望如此。
RubberDuck

3

好吧,公平地说,该文章的原始作者在他的文章中的任何地方都没有使用“ beauty”一词,这就是您,这可能给那些没有时间阅读本书的人带来偏见。他们自己。

据我所知,.NET库已经存在并且在编写时就没有考虑到COM,这可能是因为在创建该库时,并不需要支持COM。

后来,引入了支持COM的其他要求。面对这种情况,您有两种选择:

  • 重新设计原始库,使其与COM互操作兼容(可能会损坏东西)

  • 保留原始工作库的大部分内容,而是使用“包装器”(或“适配器”)的功能创建一个单独的程序集

创建包装器或适配器是一种众所周知的设计模式(在这种情况下,更多的是一种建筑模式),其优缺点也是众所周知的。一种说法是更好的“关注分离”,与美无关。

对于您的担忧

现在,您需要对.NET库和一个Interop库进行单元测试。

通过重新设计将COM接口引入现有.NET库时,您必须测试该接口。当然,引入手动编写的适配器可能需要一些其他测试来证明接口正在运行,但是不需要复制lib核心业务功能的所有单元测试。别忘了它只是lib的另一个接口。

现在,您需要花时间弄清楚如何解决漂亮的.NET库并将其公开给COM。

当您必须重新设计原始库时,您也必须弄清楚这一点,我想这将需要更多工作。

您需要有效地将班级人数增加一倍或两倍。

仅适用于通过公共COM接口公开的类,这些类应该是原始库的一小部分。

据说,对于您提到的另一种情况,当您已经知道从一开始就必须支持COM作为一等公民时,我认为您是正确的:应该避免添加单独的适配器库并设计适配器的麻烦。直接支持COM的.NET lib。


(+1)但是,“……应该是……原始库的一小部分”有时是正确的。有许多类型的项目以“类库样式”设计,其中一个类等于一个公开可见的目的,并且这些类组合起来会产生有趣的功能。在这种情况下,.NET类计数和COM互操作类计数之间可能存在一对一的映射。
rwong 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.