如何从Array.ConstrainedCopy获取InvalidCastException

时间:2013-09-18 06:22:55

标签: c# .net arrays

以下是讨论的示例代码(认为Reptile“是”动物和哺乳动物“是”动物也是如此“

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。抛出InvalidCastException的条件是:

  

sourceArray中至少有一个元素无法强制转换为类型   destinationArray。

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

7 个答案:

答案 0 :(得分: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::ArrayCopyreliable参数设置为TRUE时,使用memmove复制数组或抛出ArrayTypeMismatchException 。在任何情况下都不会抛出InvalidCastException

答案 1 :(得分:3)

老实说,我认为这只是一个复制粘贴错字;他们只是忘了把它从例外列表中删除。

答案 2 :(得分:3)

来自MSDN备注部分):

  

sourceArray 类型必须与 destinationArray 类型相同或派生自 destinationArray 类型;否则,抛出ArrayTypeMismatchException。

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

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

答案 3 :(得分: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") };

答案 4 :(得分:3)

除了ReliabilityContractAttribute之外,

ConstrainedCopy 与Array.Copy具有相同的实现。
如果我们在IL反汇编程序中打开ConstrainedCopy,我们发现它只是将其参数加载到堆栈并将它们传递给Array.Copy。 正如异常所述,Array.ConstrainedCopy在某些情况下抛出异常,而Array.Copy则不会。 ConstrainedCopy 在复制数据之前验证数组。

enter image description here

示例:常规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,所以记录抛出System.InvalidCastException的Array.ConstrainedCopy的规范是正确的,但是由于验证的优先级,我们永远不会看到InvalidCastException但是。

答案 5 :(得分:2)

以下代码会在 Mono 2.10.2.0 上抛出InvalidCastException(而问题中的代码则没有)。但是文档中的解释并不符合这种情况。

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) + ']');
}

答案 6 :(得分:1)

我偷偷进入Array类,在那里我看到Array.ContrainedCopy()只调用Array.Copy()而没有任何类型验证,因为CLR方法Array.Copy()实际上可以抛出InvalidCastException

(来自MSDN Array.Copy

  

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

我认为Array.Copy()只会将InvalidCastException提升为Array.ConstrainedCopy(),因此技术上可能Array.ConstrainedCopy()会抛出InvalidCastException,尽管不应该{0}}根据定义。