Search code examples
c#com

Unexpected C# compiler errors while attempting to wrap COM interfaces so they're IDisposable


(Note: I claim this is not a duplicate of Clean up Excel Interop Objects with IDisposable. That question asks "How do I do it?" My primary question is "Why am I getting C# compiler errors?")

Primary goal here is to read (and later write) Excel files from a C# program. Google suggests that I should add a reference to Microsoft.Office.Interop.Excel and run from there. I saw enough code to know that I'm doing COM interop, and that I therefore want to dispose the COM objects when I'm done with them. With the reference added (to version 14.0.0.0 of Microsoft.Office.Interop.Excel, in Visual Studio 2012), I'm off to the races.

Now, I don't trust myself remember to get all the calls to Marshal.ReleaseComObject in all the right places, so I want to put my COM objects into using statements. Microsoft.Office.Interop.Excel.Application is not IDisposable, and, as far as I've discovered, neither is anything else in the Microsoft.Office.Interop.Excel namespace. But that's fine. I'm smart. I know how to fix this:

using Excel = Microsoft.Office.Interop.Excel;
//...
sealed class ExcelWrapper<T> : IDisposable
{
    public T Child { get; private set; }
    public ExcelWrapper(T child) { Child = child; }
    public static implicit operator ExcelWrapper<T>(T child) { return new ExcelWrapper<T>(child); }
    public static implicit operator T(ExcelWrapper<T> host) { return host.Child; }

    public void Dispose()
    {
        try { Marshal.ReleaseComObject(Child); }
        catch { }
    }
}
//...    
using (ExcelWrapper<Excel.Application> _xlApp = (ExcelWrapper<Excel.Application>)new Excel.Application()) // CS0030
{
    Excel.Application xlApp = _xlApp; // CS0029 and CS0266
}

This presents me with error messages I don't understand (comments above indicate the lines with errors):

 error CS0030: Cannot convert type 'Microsoft.Office.Interop.Excel.Application' to 'ExcelWrapper<Microsoft.Office.Interop.Excel.Application>'
 error CS0029: Cannot implicitly convert type 'ExcelWrapper<Microsoft.Office.Interop.Excel.Application>' to 'Microsoft.Office.Interop.Excel.Application'

Why am I getting these error messages? Did I not type public static implicit operator? For some reason, I tried unsealing ExcelWrapper.Things changed, but I still don't understand what's going on:

error CS0266: Cannot implicitly convert type 'ExcelWrapper<Microsoft.Office.Interop.Excel.Application>' to 'Microsoft.Office.Interop.Excel.Application'. An explicit conversion exists (are you missing a cast?)

I'm still pretty sure an implicit conversion exists. But I can placate the compiler by using an explicit conversion, as long as I'm OK with leaving ExcelWrapper unsealed.

So, what am I doing wrong here?

Why does the compiler appear to completely ignore my conversion operators when the ExcelWrapper class is sealed? And why does the compiler appear to ignore the implicit designation (but not the rest of the operator) when the ExcelWrapper class is not sealed?


Solution

  • Section 10.9.3 of the language specification states:

    A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator. A class or struct is permitted to declare a conversion from a source type S to a target type T provided all of the following are true:

    - S and T are different types.
    - Either S or T is the class or struct type in which the operator declaration takes place.
    - Neither S nor T is object or an interface-type.
    - T is not a base class of S, and S is not a base class of T.
    

    Excel.Application is an interface type, which is why you cannot create the implicit conversion.

    The online version of the spec is a little outdated and there is an updated one in your install directory. The new section for the conversion rules is 10.10.3. For VS 2013 the spec is located at:

    C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1033
    

    In the updated version, there is also a statement of:

    • If T is an interface type, user-defined implicit conversions from S to T are ignored.

    But it is not entirely clear if that applies globally or only if an explicit conversion from S to T exists.