score:10

Accepted answer

Because you're typecasting from an int to double, it's not a conversion it's a type cast and this is somewhat different from when you cast int to double in the general case.

The Cast<T> extension method uses the IL instruction unbox.any

While a C# cast like this

var x = (double)42;

Actually results in the IL instruction

conv.r8 

Fundamentally unboxing an type as a different type is wrong and that's why you get the exception.

This question has the same answer and also links to a blog post by Eric Lippert.

score:6

list contains ints, which you attempt to cast to double. Change your query to

var query = list.Select(d => (double)d);

Update

Here's the source for Enumerable.Cast (.NET 4):

public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) { 
    IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
    if (typedSource != null) return typedSource; 
    if (source == null) throw Error.ArgumentNull("source");
    return CastIterator<TResult>(source);
}

static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
    foreach (object obj in source) yield return (TResult)obj; 
} 

As you can see, the CastIterator attempts to cast an object (which in this case is a boxed int) to a double. Unboxing operations only succeed if the target type is exactly the same as the original type that was boxed, so an exception is thrown. This link that John Leidegren provided explains in detail.

score:2

There is a difference between a .NET Framework type-cast and a C# type conversion. They are NOT the same. "Casting" int to double is a feature of the C# language, and of the language alone.

The C# compiler can convert an int to double using a special instruction for this purpose. The compiler knows, at compile time, that the source type is "int", the destination type is "double", and therefore can generate the proper instruction. This is not really a type cast in the .NET Framework sense.

System.Int32 cannot be type-cast to System.Double. The Cast extension method was compiled without knowing the exact types of the source and the destination collections, and therefore no special instructions for handling C# specific features were generated. The only code that is generated for the Cast extension method is a normal .NET type cast (casting classes to their base types, casting to object, and casting types to the interfaces they implement).


Related Articles