2016-02-15 3 views
11

Я пишу класс, который реализует интерфейсы ICollection<T> и ICollection.Как реализовать метод ICollection.CopyTo?

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

Ну 'SetValue', безусловно, бросить исключение броска для вас. – juharr

+2

Можете ли вы реализовать свой класс, взяв из ['CollectionBase'] (https://msdn.microsoft.com/en-us/library/system.collections.collectionbase_methods%28v=vs.110%29.aspx), чтобы сохранить работу ? Он реализует 'CopyTo()' для вас. –

+0

посмотрите на этот вопрос - http://stackoverflow.com/questions/5843958/how-to-make-a-copy-of-a-reference-type – harmoniemand

ответ

4

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

Есть четыре варианта для ICollection.CopyTo:

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

Мы можем обрабатывать первые два, позвонив в 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 будучи пустым, и изменить сообщение об ошибке соответственно.

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

+0

Большое спасибо, это похоже на идеальное решение. По какой-то причине я не думал о том, чтобы придать T []. Я также возьму ваш совет о том, чтобы не выполнять явно. –

+0

Если вы это сделаете, вы можете изменить довольно уродливый '((ICollection )) .CopyTo (ppArray, index)' только для 'CopyTo (ppArray, index)'. –

2

В необщего CopyTo следующих случаях может произойти:

  • Типы фактически совпадают: вызвать общую версию
  • Массив object[]: Handle этот, а, особенно, если 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."); 
} 
Смежные вопросы