9

I came across this interesting article: How I Came to Love COM Interoperability on CodeProject, which got me thinking...

The author argues that they do not want any COM-ities in their .NET library because it takes away from the beauty of their .NET library. Instead, they'd rather write a separate Interop library that exposes their .NET library to COM. This Interop library would handle the fact that COM doesn't support Constructors with Parameters, Overloaded Methods, Generics, Inheritance, Static Methods, etc.

And while I think that is very novel, doesn't it just complect the project?

  • Now you need to unit test your .NET library AND an Interop library.
  • Now you need to spend time figuring out how to work around your beautiful .NET library and expose it to COM.
  • You need to, effectively, double or triple your class count.

I can certainly understand if you need your library to support both COM and non-COM. However, if you only intend to use COM does this sort of design bring any benefits that I don't see? Do you only gain the benefits of the C# language?

Or, does it make versioning your library easier by providing you a wrapper? Does it make your Unit Tests run faster by not requiring the use of COM?

  • 2
    You mean, besides being able to use useful tools to make the real code better/cleaner? And providing a clear migration path for that (presumably legacy) COM stuff? – Telastyn Mar 24 '15 at 15:10
  • 3
    This is like an facade pattern writ large over a whole namespace. I think it is smart to organize it like this if you want to use .NET features but really need OLE Automation. Your COM wrappers will basically be to convert modern (immutable? generic?) idioms into older COM objects with parameterless constructors, heavily mutable objects, and SAFEARRAYs for your generic lists. If you are supporting other .NET components or are totally in love with the new styles this would make sense. If you are ONLY supporting COM, then why not write the whole thing in VB6 (32 bit) and keep only one idiom? – Mike Mar 24 '15 at 15:29
  • 3
    @MasonWheeler: It's deprecated in the same way all software older than 6 months is "legacy", am I right? – whatsisname Mar 24 '15 at 15:37
  • 2
    @MasonWheeler COM cannot be depreciated no matter how much people (including some groups within Microsoft) would wish it because it is still the best choice to control things out-of-process. – Mike Mar 24 '15 at 15:47
  • 2
    @Mike, I'm actually looking at porting a VB6 project to C# .NET. The user-facing APIs we use are very simple, however, the code behind the scenes is not maintainable as-is. I'm hoping to implement majority of the library in C# .NET and provide an Interop Facade that provides the simple APIs that interact with the various classes. – robodude666 Mar 24 '15 at 18:06
6

However, if you only intend to use COM does this sort of design bring any benefits that I don't see?

This snippet of your question is the most important part of your design decision here, and the answer is (as always) it depends.

If you only intend to use the library via COM Interop, then you should design the entire system with this in mind. There is no reason to triple the line count. The more LoC in the system, the more defects it will have. You should therefore design your system to only use COM compatible functionality.

However, I can't think of a good reason to restrict the use of a .Net library to COM. Why wouldn't you want to be able to use the assembly in other .Net code? It makes little sense to limit yourself this way.

I have some experience with this, so I will share how I have handled it. Its certainly not ideal. I have made some trade offs. I only use a facade for COM classes (interfaces really) that are incompatible with the newer .Net idioms, otherwise, I directly expose the .Net interface to COM. This basically means that I use a facade for any interface that uses generics. Generics are the only thing I've come across that are just simply incompatible. Unfortunately, this means a facade for anything that returns an IEnumerable<T>, which is fairly common.

However, this isn't nearly as bad as it seems to be, because your facade class inherits from your .Net class and implements a specific Interop interface. Something like this.

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

This keeps your duplicate code to an absolute minimum and protects your COM interface from "unintentional" changes. As your core class/interface change, your adapter class has to over ride more methods (or break the interface and bump the major version number). On the other hand, not all of your Interop code is stored in the Interop namespace, which can lead to a confusing file organization. Like I said, it's a trade off.

For a full blown example of this, you can browse through the SourceControl and Interop folders of my repo. ISourceControlProvider and GitProvider will be of particular interest.

  • Thanks for the response @RubberDuck! I ran across the RubberDuck project a few days ago; it's been very interesting reading through the code. Are you able to pass IEnumerable to VBA? Also, in the Rubberduck.Interop.GitProvider why do you defined parameterized constructors if COM doesn't support them? – robodude666 Apr 6 '15 at 16:16
  • Yes, you can pass an IEnumerable through COM, but it has to be the older, non-generic version. As for the constructors, take a look at the SourceControlClassFactory. Basically, you fake it by initializing an instance of a class that doesn't need parameters for its ctor and using it to gain access to new instances of your API's classes. – RubberDuck Apr 6 '15 at 16:21
  • I'm guessing that anything defined in your ComVisible(true) classes/interfaces that COM doesn't support are simply "ignored"? I'm also guessing if you have a class of the same name defined in multiple namespaces you'd have to provide a unique ProgId if you want to expose more than one of them? – robodude666 Apr 6 '15 at 17:35
  • Yes. That's right and static methods are ignored. I'm trying to see if there's an equivalent of VB6's VB_PredeclaredId available. I'm thinking it might be possible to create a default instance. – RubberDuck Apr 6 '15 at 17:38
7

it takes away from the beauty of their .NET library

That author needs a wake-up slap to the face. For any professional project, the goal is to make working software that people want to use. Any kind of misguided ideals about beauty come long after that. If that's their only reasoning, the author has lost all credibility.

Being able to mark your classes as com-visible and being able to talk to your .NET code through COM is enormously useful. Discarding that great benefit to productivity for beauty is insane.

However, making things work with COM does require some trade-offs, requiring a no-arg constructor is one of them, and some of the other stuff you mentioned. If those are features you aren't using anyway, or it won't hurt you to not use them, then there is a lot of work to be saved by using the same class for COM and non com.

On the other hand, if your COM clients expect a dramatically different interface, has dependencies or other limitations that are nasty to deal with in your .NET classes, or you expect to be significantly changing your .NET classes and need to maintain COM backward-compatibility, then by all means create an Interop class to bridge that gap.

But don't think about "beauty". Being able to expose COM through .NET is meant to save you work. Don't create more work for yourself.

  • +1 for slapping them and making them think about productivity and deliverablity. – Mike Mar 24 '15 at 15:50
  • +1. Whilst I love the beauty of black buttons on a black box practicalities are significantly more important. – gbjbaanb Mar 24 '15 at 15:55
  • Actually, the person who introduced the term "beauty" into this discussion might need a wake-up-slap, and that was not the author of that other article. – Doc Brown Apr 4 '15 at 10:37
  • 1
    Just a side note, if your current constructors require args, it's best to provide a class factory for your COM components, rather than redesign the existing interface/API. – RubberDuck Apr 4 '15 at 19:15
  • 1
    Is it possible to expose your factory as a "GlobalMultiUse" class? So you can just do Set oObject = MyCOMInterop.NewMyClass() without having to first instantiate a new Factory object? – robodude666 Apr 6 '15 at 17:22
3

Well, to be fair, the original author of that article did not use the word "beauty" anywhere in his article, that was you, which might give a biased impression on that for those who did not take the time to read the article by themselves.

As far as I have understood that article, the .NET library already existed and was written without COM in mind - probably because at the time when the library was created, there was no requirement to support COM.

Later on, the additional requirement of supporting COM was introduced. Facing such a situation, you have two options:

  • redesign the original lib to make it compatible with COM interop (with the risk of breaking things)

  • let the original working library mostly as it is, and instead create a separate assembly with the function of a "wrapper" (or "adapter")

Creating wrappers or adapters is a well-known design pattern (or in this case more an architectural pattern), and the pros and cons are also well known. One argument for it is the better "separation of concerns", and that has nothing to do with beauty.

To your concerns

Now you need to unit test your .NET library AND an Interop library.

When you introduce your COM interface to your existing .NET library by redesign, you have to test that interface either. Introducing a manually written adapter may need some additional tests that the interface is working, of course, but there is no need to copy all unit tests of the core business functionality of the lib. Don't forget it is just another interface to the lib.

Now you need to spend time figuring out how to work around your beautiful .NET library and expose it to COM.

When you have to redesign the original library you have to figure that out, too, and I guess it will be much more work.

You need to, effectively, double or triple your class count.

Only for the classes which are exposed through the public COM interface, which should be a small number of the classes part of the original library.

Said that, for a different situation you mentioned, when you already know you have to support COM as a first-class citizen right from the start, I think you are correct: one should save the hassle of adding a separated adapter lib and design the .NET lib with COM support directly.

  • (+1) However, "... which should be a small number of ... the original library" is only sometimes true. There are plenty of types of project designed in the "class library style", where one class equals one publicly visible purpose, and that classes compose to give rise to interesting functionality. In that case, there could be a one-to-one mapping between the .NET class count and the COM interop class count. – rwong Apr 5 '15 at 19:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.