Why does this Linq Cast Fail when using ToList?

前端 未结 1 873
遇见更好的自我
遇见更好的自我 2020-12-14 14:31

Consider this contrived, trivial example:

    var foo = new byte[] {246, 127};
    var bar = foo.Cast();
    var baz = new List();
         


        
相关标签:
1条回答
  • Okay, this really depends on a few oddities combined:

    • Even though in C# you can't cast a byte[] to an sbyte[] directly, the CLR allows it:

      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 such that if it thinks it doesn't need to do anything (via an is check like the above) it returns the original reference - so that's happening here.

    • ToList() delegates to the constructor of List<T> taking an IEnumerable<T>

    • That constructor is optimized for ICollection<T> to use CopyTo... and that's what's failing. Here's a version which has no method calls 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 a Select at any point, you don't end up with an ICollection<T>, so it goes through the legitimate (for the CLR) byte/sbyte conversion for each element, rather than trying to use the array implementation of CopyTo.

    0 讨论(0)
提交回复
热议问题