39

考虑这个人为的、简单的例子:

    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);
    }

借助 Two's Complement 的魔力,-10 和 127 被打印到控制台。到目前为止,一切都很好。眼睛敏锐的人会看到我正在迭代一个可枚举并将其添加到列表中。这听起来像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);
    }

除了那不起作用。我得到这个例外:

异常类型:System.ArrayTypeMismatchException

消息:源数组类型不能分配给目标数组类型。

我觉得这个例外很奇怪,因为

  1. ArrayTypeMismatchException- 我自己没有对数组做任何事情。这似乎是一个内部异常。
  2. Cast<sbyte>工作正常(如在第一个示例中),它是在使用时或ToArray问题ToList出现时。

我的目标是 .NET v4 x86,但在 3.5 中也是如此。

我不需要任何关于如何解决问题的建议,我已经设法做到了。我想知道的是为什么首先会发生这种行为?

编辑

更奇怪的是,添加一个无意义的 select 语句会导致ToList正常工作:

var baz = bar.Select(x => x).ToList();
4

1 回答 1

31

好的,这实际上取决于一些奇怪的组合:

  • 即使在 C# 中您不能直接将 abyte[]转换为a sbyte[],CLR 也允许它:

    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>()优化使得如果它认为它不需要做任何事情(通过is像上面的检查)它返回原始参考 - 所以这发生在这里。

  • ToList()委托给构造List<T>函数IEnumerable<T>

  • 该构造函数已针对ICollection<T>使用进行了优化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);
    

现在,如果您Select在任何时候使用 a ,您最终都不会得到 a ICollection<T>,因此它会通过每个元素的合法(对于 CLR)byte/sbyte转换,而不是尝试使用CopyTo.

于 2012-06-14T19:04:00.877 回答