Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to cast object of type 'System.Int32' to type 'System.Int64' [duplicate]

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) ;
like image 1000
snr Avatar asked Apr 24 '20 21:04

snr


People also ask

What is Int64 in C#?

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.

What is invalid cast exception in C#?

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.


1 Answers

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.

like image 188
David Browne - Microsoft Avatar answered Oct 09 '22 22:10

David Browne - Microsoft