Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this ToArray() extension method of IEnumerable throws ArrayTypeMismatchException?

why the next example throws a System.ArrayTypeMismatchException?

New Int16(){4,5,6}.Cast(of UInt16).ToArray()

I expected that this line returned a UInt16 array containing 4,5 and 6.

Thanks in advance.

like image 787
Ignacio Soler Garcia Avatar asked Dec 14 '09 12:12

Ignacio Soler Garcia


2 Answers

This is a bug in Cast or ToArray, IMO. The code in this answer is in C#, but hopefully you can see what it's about :)

I believe that Cast first tries to see if a simple reference conversion will work - i.e. where it can return the same reference back.

For example:

String x = "hello";
IEnumerable<char> y = x.Cast<char>();
Console.WriteLine(object.ReferenceEquals(x, y)); // Prints true

Unfortunately, it does this using the CLR rules for compatibility - under which UInt16[] and Int16[] are compatible. That leads to this happening:

short[] array = new short[]{4, 5, 6};
IEnumerable<ushort> cast = array.Cast<ushort>();
Console.WriteLine(object.ReferenceEquals(array, cast)); // Prints True

Unfortunately if you then try to call ToArray(), it's not happy:

// Explicit type argument just for clarity
cast.ToArray<ushort>(); // Bang

ToArray no doubt tries to do some optimisation - which fails in this particular case because the type isn't what it really expects it to be.

I believe the correct behaviour should be for Cast to return a lazy iterator, but for that to fail when it executes later. That's what happens if you try to go from Int16 to Int32, for example.

Now, to get back to what you really want to do: use a Select call instead. Cast is only meant to be for unboxing operations and reference type conversions.

like image 70
Jon Skeet Avatar answered Nov 05 '22 20:11

Jon Skeet


Because Int16 and UInt16 are different types. You could try this:

New Int16() {4, 5, 6}.Select(Function(x) CType(x, UInt16)).ToArray()
like image 5
Darin Dimitrov Avatar answered Nov 05 '22 22:11

Darin Dimitrov