C# 为什么使用ToList时Linq强制转换失败?
考虑一下这个做作而琐碎的例子:C# 为什么使用ToList时Linq强制转换失败?,c#,linq,casting,C#,Linq,Casting,考虑一下这个做作而琐碎的例子: 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.WriteLin
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);
}
但这不起作用。我得到一个例外:
异常类型:System.ArrayTypeMismatchException
消息:无法将源阵列类型分配给目标阵列类型
我觉得这个例外很奇怪,因为
ArrayTypeMismatchException
-我自己没有对数组做任何事情。这似乎是一个内部例外Cast
工作正常(如第一个示例中所示),当使用ToArray
或ToList
时问题就会出现ToList
正常工作:
var baz = bar.Select(x => x).ToList();
好吧,这真的取决于几个奇怪的结合:
- 即使在C#中不能将
直接强制转换为字节[]
,CLR也允许:sbyte[]
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()
检查),它将返回原始引用-这里就是这样is
委托给ToList()
的构造函数,获取List
IEnumerable
- 该构造函数针对
进行了优化,以使用ICollection
。。。这就是失败的地方。这是一个除了CopyTo
之外没有方法调用的版本:CopyTo
object bytes = new byte[] { 246, 127 }; // This succeeds... ICollection<sbyte> list = (ICollection<sbyte>) bytes; sbyte[] array = new sbyte[2]; list.CopyTo(array, 0);
objectbytes=新字节[]{246127}; //这成功了。。。 ICollection list=(ICollection)字节; sbyte[]数组=新的sbyte[2]; list.CopyTo(数组,0);
现在,如果您在任何时候使用
Select
,您都不会得到ICollection
,因此它会对每个元素进行合法的(对于CLR)字节/sbyte
转换,而不是尝试使用CopyTo
的数组实现,结果是Select
{-10127}
这里有一个铸造问题。肯定有有趣的错误消息。@Lieven是的,我收集了这么多,为什么选择(x=>x)
beforeToList
更正它?这是一个毫无意义的投影,因为同样的东西被投影回来了。我有一个解释……写出来只是花了一点时间。问得好。@vcsjones:因为这样数组帮助器就很可能不再使用了-使用Select
你正在投影到IEnumerable
+1个好问题。我喜欢涉及有趣边缘情况的问题。是的,我注意到运行时条形图的值类型是byte[],而不是System.Linq.Enumerable.CastIterator。谢谢你的解释!在阅读了问答之后,这是不是意味着。ToList需要用于类型安全转换?@Turbot:我不理解你的评论。你能重新表述一下吗?嗯,现在我明白了。Cast
在第一个或第二个示例中都没有做任何事情,而是转换是通过将其添加到列表和CLR处理转换来处理的。@JonSkeet Oops,这与类型安全转换无关。它只是与C#编译器和CLR处理转换不同的处理方式?
object bytes = new byte[] { 246, 127 };
// This succeeds...
ICollection<sbyte> list = (ICollection<sbyte>) bytes;
sbyte[] array = new sbyte[2];
list.CopyTo(array, 0);