views:

152

answers:

2

Hello,

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.

+5  A: 

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

New Int16() {4, 5, 6}.Select(Function(x) CType(x, UInt16)).ToArray()
Darin Dimitrov
That's why I'm calling Cast isn't it? If they where the same type no Cast would be needed.
SoMoS
`Cast` works only if the types are compatible (one derives from another). `Int16` and `UInt16` have nothing in common.
Darin Dimitrov
Ok, so is there a way of doing the same thing with a CType instead of with a DirectCast?
SoMoS
You could do this instead: `New Int16() {4, 5, 6}.Select(Function(x) CType(x, UInt16)).ToArray()`
Darin Dimitrov
+6  A: 

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.

Jon Skeet
Thanks, unfortunatelly I have no knowledge about LINQ still and I always have more urgents things to do, you know. Thanks again anyway.
SoMoS