There was a recent question on StackOverflow that piqued my interest and was a rather interesting problem dealing with some odd behavior someone observed with implicit operators.

Consider the following wrapper class:

sealed class Wrapper<T> : IDisposable
{
    public T Child { get; private set; }
    public Wrapper(T child) { Child = child; }
    public static implicit operator Wrapper<T>(T child) { return new Wrapper<T>(child); }

    public static implicit operator T(Wrapper<T> host) { return host.Child; }

    public void Dispose()
    {
        try { Marshal.ReleaseComObject(Child); }
        catch { }
    }
}

And the consuming code:

// DON'T DO THIS
using (Wrapper<Excel.Application> _xlApp = new Excel.Application()) 
{
    Excel.Application xlApp = _xlApp; 
}

First of all, don't do this to try and free up COM objects. The Garbage Collector will take care of it for you and you should just let it do its job.

Regardless of whether you should do it or not, the interesting part of the question is, why doesn't this compile. It gives the following errors:

  • Cannot implicitly convert type 'Microsoft.Office.Interop.Excel.Application' to 'CSharpWinForms.Wrapper<Microsoft.Office.Interop.Excel.Application>'
  • Cannot implicitly convert type 'CSharpWinForms.Wrapper<Microsoft.Office.Interop.Excel.Application>' to 'Microsoft.Office.Interop.Excel.Application'

You would expect that the implicit operators would allow the conversions between the Wrapper and the generic parameter type without issue. The key to the issue here is knowing that Excel.Application is an interface and there are different rules for conversions with interfaces.

Let's browse out to our C# spec ("C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1033\CSharp Language Specification.docx") and open it up to 10.10.3.

The two parts of the spec that are applicable here:

  • S0 and T0 are different types.

  • Either S0 or T0 is the class or struct type in which the operator declaration takes place.

  • Neither S0 nor T0 is an interface-type.

  • Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

and

  • If a pre-defined implicit conversion (§6.1) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.

  • If a pre-defined explicit conversion (§6.2) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. Furthermore:

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

This second section was added to the spec after it's initial release and the Microsoft development team obviously spent time thinking about this, as explained in this Eric Lippert answer.

Specifically he calls out the problem with interface based implicit conversion:

Why? Because one has the reasonable expectation that when one converts a value to an interface, that you are testing whether the object in question implements the interface, not asking for an entirely different object that implements the interface. In COM terms, converting to an interface is QueryInterface -- "do you implement this interface?" -- and not QueryService -- "can you find me someone who implements this interface?"

Finally he states:

However, generics muddy the waters considerably, the spec wording is not very clear, and the C# compiler contains a number of bugs in its implementation. Neither the spec nor the implementation are correct given certain edge cases involving generics, and that presents a difficult problem for me, the implementer. I am actually working with Mads today on clarifying this section of the spec, as I am implementing it in Roslyn next week.

So, in conclusion, the reason the conversion doesn't work is because that is how the language is specced and if you consider Eric's answer as to why, it does make a lot of sense.