English 中文(简体)
Enumerable.Cast<T> extension method fails to cast from int to long, why? [duplicate]
原标题:
This question already has answers here:
Closed 13 years ago.

Possible Duplicate:
Puzzling Enumerable.Cast InvalidCastException

Hi,

I just noticed something quite strange with the Enumerable.Cast<T> extension method... It seems that it can t cast from int to long, even though this cast is perfectly legal.

The following code fails with an InvalidCastException :

        foreach (var item in Enumerable.Range(0,10).Cast<long>())
        {
            Console.WriteLine(item);
        }

But this code, which I assumed to be equivalent, does work :

        foreach (var item in Enumerable.Range(0,10).Select(i => (long)i))
        {
            Console.WriteLine(item);
        }

Can anyone explain that behavior ? I looked at the code of the Cast method with Reflector, but Reflector can t interprete iterator blocks, so it s pretty hard to understand...

最佳回答

The relevant line in Cast:

 this.<>2__current = (TResult)this.<obj>5__ab;

We can mimic this using the following code:

int foo = 1;
long bar = Cast<long>(foo); //oh noes!

T Cast<T>(object input)
{
    return (T)input;
}

Which also fails. The key here is that at the point of cast, it s an object. Not an int. This fails because we can only unbox from an object to the exact type we want. We are going from object - which could be a boxed long, but it s not. It s a boxed int. Eric Lippert discussed this on his blog:

we’ve decided that unboxing can only unbox to the exact type. If you want to call the slow method that does all that goo, it’s available – you can always call Convert...

In your code which works, you re not dealing with a boxed int (an object), you ve got an int.

问题回答

Unlike most of the other LINQ extension methods, Cast extends the non-generic IEnumerable interface, rather than IEnumerable<T>.

This means that the int values generated by the Range call are boxed by the Cast call s underlying enumerator, which then attempts to cast them to long and fails, because values can only be unboxed to the exact same type.

You can mimic the same exception behaviour in your second loop by explicitly boxing the int values:

foreach (var item in Enumerable.Range(0, 10).Select(i => (long)(object)i))
{
    Console.WriteLine(item);
}

The problem is that CastIterator s MoveNext boxes the current value and attempts to unbox it to the target type (where the boxed value is not of the correct type) thus the unboxing fails during type checking.

Reference Info:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.unbox_any.aspx

    L_003c: ldarg.0 
    L_003d: ldarg.0 
    L_003e: ldfld class [mscorlib]System.Collections.IEnumerator System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<>7__wrapac
    L_0043: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
    L_0048: stfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab
    L_004d: ldarg.0 
    L_004e: ldarg.0 
    L_004f: ldfld object System.Linq.Enumerable/<CastIterator>d__aa<!TResult>::<obj>5__ab
    L_0054: unbox.any !TResult

The workaround is to use a Select()





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签