Search code examples
c#linqcastingimplicit-conversioncoercion

Why does the Linq Cast<> helper not work with the implicit cast operator?


Please read to the end before deciding of voting as duplicate...

I have a type that implements an implicit cast operator to another type:

class A
{
    private B b;
    public static implicit operator B(A a) { return a.b; }
}
class B
{
}

Now, implicit and explicit casting work just fine:

B b = a;
B b2 = (B)a;

...so how come Linq's .Cast<> doesn't?

A[] aa = new A[]{...};
var bb = aa.Cast<B>();  //throws InvalidCastException

Looking at the source code for .Cast<>, there's not much magic going on: a few special cases if the parameter really is a IEnumerable<B>, and then:

foreach (object obj in source) 
    yield return (T)obj; 
    //            ^^ this looks quite similar to the above B b2 = (B)a;

So why does my explicit cast work, but not the one inside .Cast<>?

Does the compiler sugar-up my explicit cast ?

PS. I saw this question but I don't think its answers really explain what's going on.


Solution

  • The short answer would be simply: the Cast<T> method doesn't support custom conversion operators.

    In the first example:

    B b = a;
    B b2 = (B)a;
    

    the compiler can see this B(A a) operator during static analysis; the compiler interprets this as a static call to your custom operator method. In the second example:

    foreach (object obj in source) 
        yield return (T)obj; 
    

    that has no knowledge of the operator; this is implemented via unbox.any (which is the same as castclass if T is a ref-type).

    There is also a third option: if you went via dynamic, the runtime implementation tries to mimic compiler rules, so this will find the operator ... but not as part of the C#-to-IL compile step:

    dynamic b = a; // note that `dynamic` here is *almost* the same as `object`
    B b2 = b;