19

这是讨论的示例代码(考虑爬行动物“是”动物和哺乳动物“也是”动物)

Animal[] reptiles = new Reptile[] 
    { new Reptile("lizard"), new Reptile("snake") };

Animal[] animals = new Animal[]
    { new Reptile("alligator"), new Mammal("dolphin") };

try
{
  Array.ConstrainedCopy(animals, 0, reptiles, 0, 2);
}
catch (ArrayTypeMismatchException atme)
{
  Console.WriteLine('[' + String.Join<Animal>(", ", reptiles) + ']');
}

当我运行此代码时,我得到一个 ArrayTypeMismatchException,并带有注释

Array.ConstrainedCopy 仅适用于可证明兼容的数组类型,无需对每个数组元素进行任何形式的装箱、拆箱、扩展或强制转换。更改数组类型(即,将 Derived[] 复制到 Base[]),或在 CER 中为 Array.Copy 不太强大的可靠性合约使用缓解策略,例如克隆数组或丢弃可能损坏的目标数组。

但是,当我查看MSDN时,我发现此方法也会引发InvalidCastException. 抛出 an 的条件InvalidCastException是:

sourceArray 中的至少一个元素不能转换为destinationArray 的类型。

所以我很困惑,你如何这个方法中得到一个 InvalidCastException,如果它声明永远不会有任何数组元素的转换?

4

7 回答 7

7

如果无法访问 . 的实际本地实现Array.Copy,我们可能做的最好的事情就是检查Shared Source CLI。以下是 clr\src\vm\comsystem.cpp 中的相关代码行:

FCIMPL6(void, SystemNative::ArrayCopy, ArrayBase* m_pSrc, INT32 m_iSrcIndex, ArrayBase* m_pDst, INT32 m_iDstIndex, INT32 m_iLength, CLR_BOOL reliable)
{
    // ...

    r = CanAssignArrayTypeNoGC(gc.pSrc, gc.pDst);

    if (r == AssignWrongType) {
        // [Throw ArrayTypeMismatchException]
    }

    if (r == AssignWillWork) {
        // [Copy the array using memmove, which won't throw any exception]
        return;
    }
    else if (reliable) {
        // [Throw ArrayTypeMismatchException]
    }

    // [Handle other cases]
}

当参数设置为Array.ConstrainedCopy调用SystemNative::ArrayCopy时,使用复制或抛出数组。在任何情况下都不会被抛出。reliableTRUEmemmoveArrayTypeMismatchExceptionInvalidCastException

于 2013-09-26T16:04:01.137 回答
3

老实说,我认为这只是复制粘贴的错字;他们只是忘记将其从例外列表中删除。

于 2013-09-20T08:21:56.133 回答
3

来自MSDN备注部分):

sourceArray类型必须与destinationArray 类型相同或派生自destinationArray类型;否则,将引发 ArrayTypeMismatchException。

在您的示例中,数组类型与animals数组类型不同或派生自reptiles数组类型( anAnimal不是 a Reptile)。这ArrayTypeMismatchExcetion就是抛出 an 的原因。

根据上述条件和您示例中的异常消息,可以得出结论,InvalidCastException在调用Array.ConstrainedCopy方法时无法获取。这是文档中的错误。

于 2013-09-24T20:54:18.310 回答
3

堆中第一个数组的实际类型是 Reptile[]。

Animal[] reptiles = new Reptile[] { ... };
//IL_0002:  newarr     Reptile

第二个数组:

Animal[] animals = new Animal[] { ... };
// IL_0025:  newarr     Animal

没有从 Reptile[] 到 Animal[] 的转换。所以这是 Array.ConstrainedCopy() 方法的正确行为。

此代码将正常工作:

    Animal[] reptiles = new Animal[] { new Reptile("lizard"), new Reptile("snake") };

    Animal[] animals = new Animal[] { new Reptile("alligator"), new Mammal("dolphin") };
于 2013-09-26T14:03:40.783 回答
3

ConstrainedCopy与 Array.Copy 具有相同的实现,除了ReliabilityContractAttribute.
如果我们在 IL Disassembler 中打开 ConstrainedCopy,我们会看到它所做的只是将其参数加载到堆栈中并将它们传递给 Array.Copy。
如异常所述,Array.ConstrainedCopy在 Array.Copy 没有的某些情况下会引发异常。 在复制数组之前ConstrainedCopy 验证数组。

在此处输入图像描述

示例:常规的 Array.Copy 方法将默默地复制一个字节数组到一个 int 数组。Array.ConstrainedCopy 方法会引发异常。这可以提高可靠性。

class Program
{
    static void Main()
    {
    byte[] original = new byte[10];
    original[0] = 1;

    int[] destination = new int[10];

    // This will work if you call Array.Copy instead.
    Array.ConstrainedCopy(original, 0, destination, 0, original.Length);
    }
}

顺便说一句:
Array.ConstrainedCopy 不允许进行扩大转换

结论:
Array.ConstrainedCopy 方法不允许某些副本。它比 Array.Copy 更具辨别力。它也抛出异常。通常,ConstrainedCopy 是不必要的。

关于问题
System.InvalidCastException:是通过调用 Array.Copy 可以抛出的异常之一,因为 Array.ConstrainedCopy 正在调用 Array.Copy,因此记录 Array.ConstrainedCopy 的规范是正确的抛出 System.InvalidCastException但由于验证的优先级,我们永远不会看到 InvalidCastExceptionbut。

于 2013-09-26T16:12:22.233 回答
2

以下代码InvalidCastExceptionMono 2.10.2.0上抛出(而问题中的代码没有)。但是,文档中的解释不适合这种情况。

Animal[] reptiles = new Reptile[] 
    { new Reptile("lizard"), new Reptile("snake") };

object[] animals = new object[]
    { new Reptile("alligator"), new Mammal("dolphin") };

try
{
    Array.ConstrainedCopy(animals, 0, reptiles, 0, 2);
}
catch (ArrayTypeMismatchException atme)
{
    //Console.WriteLine('[' + String.Join<Animal>(", ", reptiles) + ']');
}
于 2013-09-22T18:53:41.030 回答
1

我偷窥了Array类,我看到它Array.ContrainedCopy()只是在内部调用Array.Copy()而没有任何类型验证,因为 CLR 方法 Array.Copy() 实际上可以抛出InvalidCastException

(来自 MSDN Array.Copy

从引用类型或值类型数组复制到 Object 数组时,Object会创建 an 来保存每个值或引用,然后复制。当从Object数组复制到引用类型或值类型数组并且无法赋值时,InvalidCastException抛出 an。

我假设Array.Copy()只是将 to 提升InvalidCastExceptionArray.ConstrainedCopy(),因此从技术上讲,它可能会Array.ConstrainedCopy()抛出InvalidCastException它不应该按照定义。

于 2013-09-25T14:07:23.083 回答