Как реализовать метод ICollection.CopyTo?

Я пишу класс, который реализует интерфейсы ICollection<T> и ICollection.

MSDN говорит, что они немного разные. ICollection<T>.CopyTo принимает аргумент T[], тогда как ICollection.CopyTo принимает аргумент System.Array. Существует также разница между исключениями.

Вот моя реализация универсального метода (я считаю, что он полностью функциональный):

void ICollection<PlcParameter>.CopyTo(PlcParameter[] array, int arrayIndex)
{
    if (array == null)
        throw new ArgumentNullException("array");
    if (arrayIndex < 0)
        throw new ArgumentOutOfRangeException("arrayIndex");
    if (array.Length - arrayIndex < Count)
        throw new ArgumentException("Not enough elements after arrayIndex in the destination array.");

    for (int i = 0; i < Count; ++i)
        array[i + arrayIndex] = this[i];
}

Однако, не общий вариант метода немного меня путает. Во-первых, , как я могу проверить следующее условие исключения?

Тип источника ICollection не может быть автоматически добавлен к типу целевого массива.

Во-вторых, существует ли способ использовать существующую общую реализацию, чтобы уменьшить дублирование кода?

Вот моя работа в процессе выполнения:

void ICollection.CopyTo(Array array, int index)
{
    if (array == null)
        throw new ArgumentNullException("array");
    if (index < 0)
        throw new ArgumentOutOfRangeException("arrayIndex");
    if (array.Rank > 1)
        throw new ArgumentException("array is multidimensional.");
    if (array.Length - index < Count)
        throw new ArgumentException("Not enough elements after index in the destination array.");

    for (int i = 0; i < Count; ++i)
        array.SetValue(this[i], i + index);
}

Ответ 1

Вы уже выполнили большую часть работы по реализации ICollection<T>.CopyTo.

Существует четыре возможности для ICollection.CopyTo:

  • Он будет работать так же, как ICollection<T>.
  • Это приведет к ошибке по той причине, что ICollection<T> не удался.
  • Он будет терпеть неудачу из-за несоответствия ранга.
  • Он будет терпеть неудачу из-за несоответствия типов.

Мы можем обрабатывать первые два, вызывая в ICollection<T>.CopyTo.

В каждом из этих случаев array as PlcParameter[] даст нам ссылку на массив, строго типизированный.

В каждом из последних случаев это не будет.

Мы хотим поймать array == null отдельно, хотя:

void ICollection.CopyTo(Array array, int index)
{
  if (array == null)
    throw new ArgumentNullException("array");
  PlcParameter[] ppArray = array as PlcParameter[];
  if (ppArray == null)
    throw new ArgumentException();
  ((ICollection<PlcParameter>)this).CopyTo(ppArray, index);
}

Если вы действительно хотели, вы могли бы протестировать array.Rank == 1 в случае, если ppArray имеет значение null, и соответствующим образом изменить сообщение об ошибке.

(Кстати, почему вы явно реализуете ICollection<PlcParameter>.CopyTo? это, вероятно, достаточно, чтобы быть полезной для явной работы работы, поэтому людям не нужно бросать все это.)

Ответ 2

В не-generic CopyTo могут случиться следующие случаи:

  • Типы фактически соответствуют: вызов общей версии
  • Массив object[]: дескриптор этого, особенно если T является ссылочным типом, так как в этом случае T[] можно отнести к object[]
  • Если ваша коллекция является словарем, вы можете обрабатывать массивы KeyValuePair[] и DictionaryEntry[], а также

Итак, в общем случае вы должны использовать следующую реализацию:

void ICollection.CopyTo(Array array, int index)
{
    if (array != null && array.Rank != 1)
        throw new ArgumentException("Only single dimensional arrays are supported for the requested action.", "array");

    // 1. call the generic version
    T[] typedArray = array as T[];
    if (typedArray != null)
    {
        CopyTo(typedArray, index);
        return;
    }

    // 2. object[]
    object[] objectArray = array as object[];
    if (objectArray != null)
    {
        for (int i = 0; i < size; i++)
        {
            objectArray[index++] = GetElementAt(i);
        }
    }

    throw new ArgumentException("Target array type is not compatible with the type of items in the collection.");
}