Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement ICollection.CopyTo method?

Tags:

c#

.net

I'm writing a class that implements ICollection<T> and ICollection interfaces.

MSDN says these are a bit different. ICollection<T>.CopyTo takes a T[] argument, whereas ICollection.CopyTo takes a System.Array argument. There is also a difference between exceptions thrown.

Here is my implementation of the generic method (I believe it's fully functional):

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];
}

However, the non-generic version of the method is confusing me a bit. First, how do I check for the following exception condition?

The type of the source ICollection cannot be cast automatically to the type of the destination array.

Second, is there a way to leverage the existing generic implementation to reduce code duplication?

Here is my work-in-progress implementation:

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);
}
like image 281
relatively_random Avatar asked Feb 15 '16 14:02

relatively_random


2 Answers

You've already done most of the work in implementing ICollection<T>.CopyTo.

There are four possibilities for ICollection.CopyTo:

  1. It will work the same as ICollection<T>.
  2. It will fail for a reason ICollection<T> would have failed.
  3. It will fail because of rank-mismatch.
  4. It will fail because of type mismatch.

We can handle the first two by calling into ICollection<T>.CopyTo.

In each of these cases array as PlcParameter[] will give us a reference to the array, strongly typed.

In each of the latter cases, it won't.

We do want to catch array == null separately though:

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);
}

If you really wanted you could test array.Rank == 1 in the case of ppArray being null, and change the error message accordingly.

(BTW, why are you explicitly implementing ICollection<PlcParameter>.CopyTo? it's likely enough to be useful to be work implementing explicitly, so people don't have to cast to all it.)

like image 174
Jon Hanna Avatar answered Sep 20 '22 05:09

Jon Hanna


In the non-generic CopyTo the following cases can happen:

  • Types actually match: call the generic version
  • The array is object[]: Handle this one as well, especially if T is a reference type since in this case T[] can be cast to object[]
  • If your collection is a dictionary, you can handle the KeyValuePair[] and DictionaryEntry[] arrays as well

So in a general case you should use the following implementation:

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.");
}
like image 44
György Kőszeg Avatar answered Sep 19 '22 05:09

György Kőszeg