Cast is different than Convert. Int32
->Int64
is a semantically lossless conversion. Many C# types overload the '(type)' cast operator with a User-Definied Conversion Operator and actually perform a conversion where a type cast would not allowed.
And the C# language will generate an Implicit Conversion for you for common and safe code patterns like:
int num = 2147483647;
long bigNum = num;
But these are exceptions to the rule that a type cast requires the the object to be assignable to a variable of the target type. The other exception is that an expression of type object
may be cast to any type, and will fail at runtime if the object is not assignable to a variable of that type.
Enumerable.Cast<T>
does exactly what it says, and performs a Cast on each element in the collection. Not a conversion, even if the type implements a User-Defined Conversion Operator. This is because the cast is from object
to T
, thus bypassing any User-Defined Conversion Operator, or C# implicit conversion.
Source:
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;
}
Reference Source
Note that the argument to Cast<T>
is Enumerable
, not Enumberable<TSource>
. The intent of this API is to provide an adapter for non-generic collections. Before Generics were introduced in .NET (note they're not just a language feature), Enumerable
was the base collection type, and to use older collections with LINQ it's necessary to convert them to IEnumerable<T>
.
Even even if it were a goal of this API to apply user-defined conversions or implicit conversions, there's no obvious way in C# to implement it. C# generics aren't templates*, and the generic method will have a single implementation that has to follow the rules of C#. EG something like this;
public static IEnumerable<TDest> MyCast<TDest, TSource>(this IEnumerable<TSource> col)
{
foreach (var s in col)
yield return (TDest)s;
}
Can't compile, as the compiler can't verify that TDest is assignable from an object of type TSource. So you you would have to cast each item to object
first, which is exactly what happens in the version that takes the non-generic IEnumerable.
You could write something like
yield return (TDest)Convert.ChangeType(s, typeof(TDest));
But then the method should be:
public static class MyConvertExtension
{
public static IEnumerable<TDest> Convert<TDest, TSource>(this IEnumerable<TSource> col)
{
foreach (var s in col)
yield return (TDest)Convert.ChangeType(s, typeof(TDest));
}
}
*Differences Between C++ Templates and C# Generics
C++ allows code that might not be valid for all type parameters in the
template, which is then checked for the specific type used as the type
parameter. C# requires code in a class to be written in such a way
that it will work with any type that satisfies the constraints. For
example, in C++ it is possible to write a function that uses the
arithmetic operators + and - on objects of the type parameter, which
will produce an error at the time of instantiation of the template
with a type that does not support these operators. C# disallows this;
the only language constructs allowed are those that can be deduced
from the constraints.