If the error were unable to cast object of type System.Int64
to type System.Int32
, I would understand that it is due to the possibility of data narrowing. However, I don't understand why there is the error, unable to cast object of type System.Int32
to type System.Int64
, albeit the range of long
is higher.
int[] integers = { 1, 2, 3 };
IEnumerable<long> test1 = integers.OfType<long>();
IEnumerable<long> test2 = integers.Cast<long>();
foreach (var l in test2) ;
Int64 is an immutable value type that represents signed integers with values that range from negative 9,223,372,036,854,775,808 (which is represented by the Int64. MinValue constant) through positive 9,223,372,036,854,775,807 (which is represented by the Int64. MaxValue constant.
An InvalidCastException exception is thrown when the conversion of an instance of one type to another type is not supported. For example, attempting to convert a Char value to a DateTime value throws an InvalidCastException exception.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With