Подтвердить что ты не робот

Как получить InvalidCastException из Array.ConstrainedCopy

Вот пример кода для обсуждения (см. "Рептилия" - "Животное и млекопитающее" - это "Животное тоже" )

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 [] на базу []) или использовать стратегию смягчения в CER для Array.Copy's менее мощный контракт на надежность, например, клонирование массива или отбрасывая потенциально поврежденный целевой массив.

Однако, когда я смотрю MSDN, я вижу, что этот метод также генерирует InvalidCastException. Условие throw InvalidCastException:

По крайней мере один элемент в sourceArray не может быть отнесен к типу destinationArray.

Итак, я в тупике, как вы получаете InvalidCastException из этого метода, если в нем указано, что никогда не может быть никакого литья элемента массива?

4b9b3361

Ответ 1

Без доступа к фактической встроенной реализации Array.Copy лучшее, что мы, возможно, можем сделать, это изучить 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 с параметром reliable, установленным на TRUE, либо копируется массив с использованием memmove, либо ArrayTypeMismatchException. В любом случае не будет выбрано InvalidCastException.

Ответ 2

Честно говоря, я думаю, что это просто опечатка для копирования-вставки; они просто забыли удалить его из списка исключений.

Ответ 3

Из MSDN (раздел Замечания):

Тип sourceArray должен быть таким же или полученным из типа destinationArray; в противном случае создается исключение ArrayTypeMismatchException.

В вашем примере тип массива animals не совпадает с производным от типа массива reptiles (или Animal не является Reptile). Вот почему бросается ArrayTypeMismatchExcetion.

Исходя из приведенных выше условий и сообщения об исключении из вашего примера, можно сделать вывод, что при вызове метода Array.ConstrainedCopy нет способа получить InvalidCastException. Это ошибка в документации.

Ответ 4

Фактическим типом первого массива в куче является Reptile [].

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

Второй массив:

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

От Рептилии [] до Животного [] нет никакого эффекта. Таким образом, это правильное поведение метода Array.ConstrainedCopy().

Этот код будет работать правильно:

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

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

Ответ 5

ConstrainedCopy имеет ту же реализацию, что и Array.Copy, за исключением ReliabilityContractAttribute.
Если мы откроем ConstrainedCopy в IL Disassembler, мы увидим, что все, что он делает, это загрузить свои аргументы в стек и передать их в 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);
    }
}

BTW:
Array.ConstrainedCopy не позволяет выполнять расширенное преобразование.

Вывод:
Метод Array.ConstrainedCopy не позволяет выполнять определенные копии. Это более дискриминационно, чем Array.Copy. Он также генерирует исключения. Часто ConstrainedCopy не требуется.

О вопросе:
System.InvalidCastException: Является одним из исключений, которые могут быть выбраны путем вызова Array.Copy, поскольку Array.ConstrainedCopy вызывает Array.Copy. Правильно документировать выделение Array.ConstrainedCopy, бросая System.InvalidCastException, но из-за приоритета проверки мы никогда не увидим InvalidCastExceptionbut.

Ответ 6

Следующий код выдает InvalidCastException в Моно 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) + ']');
}

Ответ 7

Я заглянул в класс Array, где увидел, что Array.ContrainedCopy() просто вызывает Array.Copy() внутри без какой-либо проверки типов, где, как метод CLR Array.Copy(), действительно может выбросить InvalidCastException:

(из MSDN Array.Copy

При копировании из массива ссылочного типа или массива значений в массив объектов создается Object для хранения каждого значения или ссылки и последующего копирования. При копировании из массива Object в массив ссылочного типа или массива значений и назначение не возможно, создается InvalidCastException.

Я предполагаю, что Array.Copy() просто увеличивает значение InvalidCastException до Array.ConstrainedCopy(), поэтому технически возможно, что Array.ConstrainedCopy() выбрасывает InvalidCastException, хотя это не должно быть в определении.