Why is this Linq Cast Fail when using ToList?

Consider this contrived, trivial example:

var foo = new byte[] {246, 127}; var bar = foo.Cast<sbyte>(); var baz = new List<sbyte>(); foreach (var sb in bar) { baz.Add(sb); } foreach (var sb in baz) { Console.WriteLine(sb); } 

With the magic of Two Complement, -10 and 127 are printed on the console. So far, so good. People with keen eyes will see me repeat the enumerated amount and add it to the list. This sounds like a ToList :

  var foo = new byte[] {246, 127}; var bar = foo.Cast<sbyte>(); var baz = bar.ToList(); //Nothing to see here foreach (var sb in baz) { Console.WriteLine(sb); } 

Except that this does not work. I get this exception:

Exception Type: System.ArrayTypeMismatchException

Message: The type of the source array cannot be assigned to the type of the target array.

I find this exception very peculiar because

  • ArrayTypeMismatchException - I do nothing with arrays. This is apparently an internal exception.
  • Cast<sbyte> works fine (as in the first example), when using ToArray or ToList problem occurs.

I target .NET v4 x86, but the same thing happens in 3.5.

I do not need any advice on how to solve the problem, I already managed to do it. I want to know why this happens in the first place?

EDIT

Even stranger, adding a meaningless select statement, the ToList works correctly:

 var baz = bar.Select(x => x).ToList(); 
+34
casting c # linq
Jun 14 '12 at 18:48
source share
1 answer

Well, it really depends on a few oddities:

  • Even if in C # you cannot directly pass byte[] to sbyte[] , the CLR allows this:

     var foo = new byte[] {246, 127}; // This produces a warning at compile-time, and the C# compiler "optimizes" // to the constant "false" Console.WriteLine(foo is sbyte[]); object x = foo; // Using object fools the C# compiler into really consulting the CLR... which // allows the conversion, so this prints True Console.WriteLine(x is sbyte[]); 
  • Cast<T>() optimizes so that if it thinks it doesn’t need anything (using the is check, as mentioned above), it returns the original link - so what happens here.

    / li>
  • ToList() delegates to the List<T> constructor using IEnumerable<T>

  • This constructor is optimized for ICollection<T> to use CopyTo ... and this is what fails. Here is a version that has no methods other than CopyTo :

     object bytes = new byte[] { 246, 127 }; // This succeeds... ICollection<sbyte> list = (ICollection<sbyte>) bytes; sbyte[] array = new sbyte[2]; list.CopyTo(array, 0); 

Now, if you use Select at any point, you will not get ICollection<T> , so it goes through the legal (for CLR) byte / sbyte for each element, instead of trying to use the CopyTo array CopyTo .

+26
Jun 14 2018-12-12T00:
source share



All Articles