Question

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.

Was it helpful?

Solution 2

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;

OTHER TIPS

So why my explicit cast work, and the one inside .Cast<> doesn't?

Your explicit cast knows at compile time what the source and destination types are. The compiler can spot the explicit conversion, and emit code to invoke it.

That isn't the case with generic types. Note that this isn't specific to Cast or LINQ in general - you'd see the same thing if you tried a simple Convert method:

public static TTarget Convert<TSource, TTarget>(TSource value)
{
    return (TTarget) value;
}

That will not invoke any user-defined conversions - or even conversions from (say) int to long. It will only perform reference conversions and boxing/unboxing conversions. It's just part of how generics work.

Enumerable.Cast<T> is a .Net framework method and has a behavior that makes sense in all of the .Net languages that call it.

See also, Ander Hejlsberg's reply on this discussion.


Does the compiler sugar-up my explicit cast ?

What you call an "implicit cast operator" is actually an "implicit conversion operator". It's a common mistake.

C# allows you to specify conversions using the casting syntax. When this happens, you are using a different instance (converting), not changing the reference to the same instance (casting).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top