I really hate giving you an "it depends" answer... but it depends!
Generally relying on the generated Interop file is fine if your project setup is quite trivial. When you have a lot of projects and/or when you use strong naming, then things can get a little trickier. In our case, we basically have strong naming for everything, and this includes the Interop files. We compile our projects via a script on our integration server, and generate all of our Interop files on the fly when we need them, but before we compile our project source. In this way, we don't have to think about all of those edge cases where accessing assemblies might not work. In this case it looks like we're implementing your second option, and usually don't bother with the first option unless it's something very trivial and we don't need to rely on strong naming or on a prior build - for whatever reason.
As for the 3rd option, I can't imagine how you'd ever need to create your own Interop files given you've got 2 other options at your disposal which do the task much more easily for you.
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.
Best Answer
Before .NET 4.0, VB.NET had a slight advantage over C# when calling COM interop code. This was especially true for code, such as the Office programmability model which, as mortalapeman notes, makes use of COM's support for optional parameters. This API often exposed methods with 10, 15 or more parameters (ugh!), all of which were
ref
params, which in C# had to be specified as references toMissing.Value
. This was a pain.It's important to note, though, that while this was common when calling Office code, I never really encountered it anywhere else. Most COM APIs I worked with were a lot more straightforward, and could be called from C# with little hassle.
However, in .NET 4.0, even this small problem has been corrected. For general purpose COM APIs, you can use the
dynamic
keyword to call late-bound code without the strong type checking C# usually demands, and thus call COM APIs without a hitch. Check out this article on general usages ofdynamic
, and this one specifically for working with COM objects.If you're planning on using this to interop with Office components, C# 4.0 offers an even simpler solution. Check out this MSDN article on Office's optional parameters:
So instead of calling
myWordDoc.CheckSpelling()
with 12 (count'em, 12!) pointless optional parameters, you can callmyWordDoc.InnerObject.CheckSpelling(IgnoreUpperCase: true)
, clean and neat.